import {z} from 'zod' export type GetApiV1ShortSqueezeScoresArgs = {minMarketCap?: number | undefined, minDollarVolume?: number | undefined, ticker?: string | undefined, limit?: number | undefined, offset?: number | undefined}; export type GetApiV1ShortSqueezeScoresOptions = {baseUrl?: string | undefined, ApiKey?: string | undefined, fetch?: typeof fetch | undefined}; export const shortSqueezeScoreDto = z.object({ticker: z.string().nullable().optional(), rank: z.number().int().nullable().optional(), score: z.number().optional(), shortInterestPercentOfShares: z.number().optional(), daysToCover: z.number().nullable().optional(), shortVolumeShareTrend: z.number().nullable().optional(), shortInterestChangePercent: z.number().nullable().optional(), failsToDeliverPercentOfShares: z.number().nullable().optional(), priceAboveVwap: z.number().nullable().optional(), hasPriceSpikeCatalyst: z.boolean().optional(), hasVolumeSurgeCatalyst: z.boolean().optional(), hasEarningsProximityCatalyst: z.boolean().optional(), marketCapitalization: z.number().nullable().optional(), averageDailyDollarVolume: z.number().nullable().optional(), baseScore: z.number().optional(), catalystBoost: z.number().optional(), shortInterestPercentile: z.number().optional(), daysToCoverPercentile: z.number().nullable().optional(), shortVolumeTrendPercentile: z.number().nullable().optional(), shortInterestChangePercentile: z.number().nullable().optional(), failsToDeliverPercentile: z.number().nullable().optional(), priceAboveVwapPercentile: z.number().nullable().optional()}); export const shortSqueezeScoresResponse = z.object({settlementDate: z.string().nullable().optional(), data: z.array(shortSqueezeScoreDto).nullable().optional(), rank: z.number().int().nullable().optional(), scoredCount: z.number().int().nullable().optional(), total: z.number().int().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 }; /** * Highest composite short-squeeze scores — a peer-relative 0-100 rank: a weighted mean of six factor percentiles (short interest % of shares 30%, days to cover 20%, price vs trailing VWAP 15%, short-volume trend 15%, short-interest change 10%, fails-to-deliver pressure 10%) plus catalyst boosts for an unusual price spike, volume surge, or a scheduled earnings call within a few weekdays (+10 each, capped at +20, clamped to 100), highest score first. Untradeable micro-caps dominate the raw board; pass minMarketCap and/or minDollarVolume to keep only names clearing your liquidity bar (scores stay peer-relative to the full universe). * * GET /v1/short-squeeze-scores */ export const getApiV1ShortSqueezeScores = async (args: GetApiV1ShortSqueezeScoresArgs, options?: GetApiV1ShortSqueezeScoresOptions) => { 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 (args.minMarketCap != null) query.append("minMarketCap", String(args.minMarketCap)) if (args.minDollarVolume != null) query.append("minDollarVolume", String(args.minDollarVolume)) if (args.ticker != null) query.append("ticker", String(args.ticker)) if (args.limit != null) query.append("limit", String(args.limit)) if (args.offset != null) query.append("offset", String(args.offset)) if (ApiKey !== undefined) headers.set('authorization', `Bearer ${ApiKey}`) const url = `${baseUrl.replace(/\/+$/, '')}/v1/short-squeeze-scores${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(shortSqueezeScoresResponse, await res.json()) };