import {z} from 'zod' export type GetApiApiV1EquitiesSymbolEarningsPeadArgs = {symbol: string, lookback?: number | undefined, market_proxy?: string | undefined, drift_window_end?: number | undefined, include_daily_ar?: boolean | undefined}; export type GetApiApiV1EquitiesSymbolEarningsPeadOptions = {baseUrl?: string | undefined, APIKeyHeader?: string | undefined, fetch?: typeof fetch | undefined}; export const marketModelFit = z.object({alpha: z.number(), beta: z.number(), r_squared: z.number().gte(0).lte(1), n_obs: z.number().int(), model_degenerate: z.boolean()}); export const peadEventResult = z.object({announcement_date: z.string().nullable().optional(), event_day0_date: z.string().nullable().optional(), period: z.string().nullable().optional(), year: z.number().int().nullable().optional(), quarter: z.number().int().nullable().optional(), filing_window: z.string().nullable().optional(), event_timing_assumed: z.boolean().nullable().optional(), surprise_label: z.string().nullable().optional(), surprise_pct: z.number().nullable().optional(), surprise_period: z.string().nullable().optional(), surprise_lag_days: z.number().int().nullable().optional(), surprise_match_status: z.string().nullable().optional(), unknown_reason: z.string().nullable().optional(), bucket: z.string().nullable().optional(), market_model: marketModelFit.nullable().optional(), reaction_car: z.record(z.string(), z.number().nullable()).nullable().optional(), drift_car: z.record(z.string(), z.number().nullable()).nullable().optional(), overlaps_next_event: z.record(z.string(), z.boolean()).nullable().optional(), daily_ar: z.array(z.number()).nullable().optional(), skip_reason: z.string().nullable().optional()}); export const peadWindowStats = z.object({window: z.array(z.number().int()), n: z.number().int().gte(0), mean_car: z.number().nullable().optional(), std_car: z.number().nullable().optional(), t_stat: z.number().nullable().optional(), p_value: z.number().nullable().optional(), p_value_method: z.string().optional(), ci_low: z.number().nullable().optional(), ci_high: z.number().nullable().optional(), exploratory: z.boolean().optional()}); export const peadBucketAggregate = z.object({bucket: z.string(), n_events: z.number().int().gte(0), inference_unreliable: z.boolean(), windows: z.record(z.string(), peadWindowStats)}); export const peadSpread = z.object({drift_window: z.string(), beat_mean_car: z.number().nullable().optional(), miss_mean_car: z.number().nullable().optional(), spread: z.number().nullable().optional(), n_beat: z.number().int().gte(0), n_miss: z.number().int().gte(0), n_unknown: z.number().int().gte(0)}); export const peadPayload = z.object({symbol: z.string(), market_proxy: z.string(), interval: z.string(), estimation_window: z.array(z.number().int()), min_estimation_days: z.number().int(), reaction_window: z.array(z.number().int()), drift_windows: z.array(z.array(z.number().int())), event_window: z.array(z.number().int()), include_daily_ar: z.boolean(), conditioning_status: z.string(), coverage: z.record(z.string(), z.unknown()), events: z.array(peadEventResult), by_bucket: z.record(z.string(), peadBucketAggregate), pead_spread: z.array(peadSpread), small_sample_warning: z.boolean(), descriptive_note: z.string(), point_in_time_note: z.string(), source: z.string().optional()}); export const sugraMeta = z.object({endpoint: z.string(), data_time: z.string(), response_time: z.string(), provider: z.string(), data_age_days: z.number().nullable().optional(), source: z.string().nullable().optional(), attribution: z.string().nullable().optional(), source_url: z.string().nullable().optional(), citation: z.string().nullable().optional(), fallback_used: z.boolean().nullable().optional(), fallback_chain: z.array(z.string()).nullable().optional(), cached: z.boolean().nullable().optional(), stale: z.boolean().nullable().optional(), stale_since: z.string().nullable().optional(), period: z.string().nullable().optional(), notes: z.string().nullable().optional()}); export const envelopePeadPayload = z.object({data: peadPayload, meta: sugraMeta}); 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 }; /** * Post-earnings-announcement drift (PEAD) conditioned on EPS surprise * * GET /api/v1/equities/{symbol}/earnings/pead */ export const getApiApiV1EquitiesSymbolEarningsPead = async (args: GetApiApiV1EquitiesSymbolEarningsPeadArgs, options?: GetApiApiV1EquitiesSymbolEarningsPeadOptions) => { const {baseUrl = "https://sugra.ai", fetch: fetchFn = fetch, APIKeyHeader} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.lookback != null) query.append("lookback", String(args.lookback)) if (args.market_proxy != null) query.append("market_proxy", String(args.market_proxy)) if (args.drift_window_end != null) query.append("drift_window_end", String(args.drift_window_end)) if (args.include_daily_ar != null) query.append("include_daily_ar", String(args.include_daily_ar)) if (APIKeyHeader !== undefined) headers.set("x-api-key", APIKeyHeader) const url = `${baseUrl.replace(/\/+$/, '')}/api/v1/equities/${encodeURIComponent(String(args.symbol))}/earnings/pead${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(envelopePeadPayload, await res.json()) };