import {z} from 'zod' export type GetApiV1StocksTickerShortInterestForecastArgs = {ticker: string}; export type GetApiV1StocksTickerShortInterestForecastOptions = {baseUrl?: string | undefined, ApiKey?: string | undefined, fetch?: typeof fetch | undefined}; export const shortInterestForecastResponse = z.object({ticker: z.string().nullable().optional(), settlementDate: z.string().optional(), publicationDate: z.string().optional(), previousSettlementDate: z.string().optional(), previousReportedPosition: z.number().int().optional(), estimatedShortPosition: z.number().int().optional(), estimatedChangePercent: z.number().optional(), likelyRangeLow: z.number().int().optional(), likelyRangeHigh: z.number().int().optional(), confidence: z.string().nullable().optional(), riseProbability: z.number().optional(), expectsIncrease: z.boolean().optional(), asOfDate: z.string().optional(), note: z.string().nullable().optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * The model's estimate of the next short-interest settlement — the one FINRA has already measured but has not published yet. This is a PREDICTION, not a reported figure: never cite it as FINRA data. It carries a likely range (P10-P90) and a confidence band, and it is withdrawn as soon as the official figure for that settlement exists. Returns 404 when there is nothing to forecast, which is the normal state in the days after a release. * * GET /v1/stocks/{ticker}/short-interest/forecast */ export const getApiV1StocksTickerShortInterestForecast = async (args: GetApiV1StocksTickerShortInterestForecastArgs, options?: GetApiV1StocksTickerShortInterestForecastOptions) => { const {baseUrl = "https://api.equibles.com", fetch: fetchFn = fetch, ApiKey} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (ApiKey !== undefined) headers.set('authorization', `Bearer ${ApiKey}`) const url = `${baseUrl.replace(/\/+$/, '')}/v1/stocks/${encodeURIComponent(String(args.ticker))}/short-interest/forecast${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(shortInterestForecastResponse, await res.json()) };