import {z} from 'zod' export type Period = 'annual' | 'quarter' | 'ttm'; export type GetApiApiFinancialsKeyMetricsArgs = {symbol?: string | undefined, cik?: number | undefined, cusip?: string | undefined, composite_figi?: string | undefined, share_class_figi?: string | undefined, currency?: string | undefined, period?: Period | undefined, limit?: number | undefined}; export type GetApiApiFinancialsKeyMetricsOptions = {baseUrl?: string | undefined, bearerAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const fiscalPeriod = z.enum(["FY", "Q1", "Q2", "Q3", "Q4", "TTM"]); export const keyMetricsGeneral = z.object({revenue: z.number().nullable().optional(), netIncome: z.number().nullable().optional(), grossMargin: z.number().nullable().optional(), operatingMargin: z.number().nullable().optional(), profitMargin: z.number().nullable().optional(), debtToEquity: z.number().nullable().optional(), quickRatio: z.number().nullable().optional(), currentRatio: z.number().nullable().optional(), roa: z.number().nullable().optional(), roe: z.number().nullable().optional(), roic: z.number().nullable().optional(), eps: z.number().nullable().optional(), ebitda: z.number().nullable().optional(), freeCashFlow: z.number().nullable().optional(), fcfMargin: z.number().nullable().optional(), fcfNetIncome: z.number().nullable().optional(), assetTurnover: z.number().nullable().optional(), interestCoverage: z.number().nullable().optional(), netDebt: z.number().nullable().optional(), netDebtEbitda: z.number().nullable().optional(), investedCapital: z.number().nullable().optional(), sbcRevenue: z.number().nullable().optional(), dividendPayout: z.number().nullable().optional(), netShareChange: z.number().nullable().optional(), cashRatio: z.number().nullable().optional(), operatingCashFlowRatio: z.number().nullable().optional(), receivablesTurnover: z.number().nullable().optional(), payablesTurnover: z.number().nullable().optional(), inventoryTurnover: z.number().nullable().optional(), fixedAssetTurnover: z.number().nullable().optional(), financialLeverageRatio: z.number().nullable().optional(), longTermDebtToCapitalRatio: z.number().nullable().optional(), freeCashFlowOperatingCashFlowRatio: z.number().nullable().optional(), capitalExpenditureCoverageRatio: z.number().nullable().optional(), pretaxProfitMargin: z.number().nullable().optional(), effectiveTaxRate: z.number().nullable().optional(), revenuePerShare: z.number().nullable().optional(), netIncomePerShare: z.number().nullable().optional(), operatingCashFlowPerShare: z.number().nullable().optional(), freeCashFlowPerShare: z.number().nullable().optional(), cashPerShare: z.number().nullable().optional(), bookValuePerShare: z.number().nullable().optional()}); export const keyMetricsSector = z.object({affo: z.number().nullable().optional(), allowancePctAr: z.number().nullable().optional(), allowanceToLoans: z.number().nullable().optional(), aroCurrent: z.number().nullable().optional(), aroNoncurrent: z.number().nullable().optional(), assetTurnover: z.number().nullable().optional(), bondingCapacity: z.number().nullable().optional(), bookValueGrowth: z.number().nullable().optional(), capexDda: z.number().nullable().optional(), capexDepreciation: z.number().nullable().optional(), capexRevenue: z.number().nullable().optional(), ccc: z.number().nullable().optional(), cipRatio: z.number().nullable().optional(), combinedRatio: z.number().nullable().optional(), contractAssetRatio: z.number().nullable().optional(), contractLiabilityRatio: z.number().nullable().optional(), dacRatio: z.number().nullable().optional(), daysInventory: z.number().nullable().optional(), debtEbitda: z.number().nullable().optional(), debtEquity: z.number().nullable().optional(), debtToAssets: z.number().nullable().optional(), debtToEquity: z.number().nullable().optional(), dio: z.number().nullable().optional(), dividendCoverage: z.number().nullable().optional(), dividendPayout: z.number().nullable().optional(), dpo: z.number().nullable().optional(), dso: z.number().nullable().optional(), ebitdaMargin: z.number().nullable().optional(), efficiencyRatio: z.number().nullable().optional(), expenseRatio: z.number().nullable().optional(), ffo: z.number().nullable().optional(), ffoPerShare: z.number().nullable().optional(), floatSize: z.number().nullable().optional(), floatYield: z.number().nullable().optional(), grossMargin: z.number().nullable().optional(), incomePerStore: z.number().nullable().optional(), insuranceLeverage: z.number().nullable().optional(), interestCoverage: z.number().nullable().optional(), interestExpense: z.number().nullable().optional(), interestIncome: z.number().nullable().optional(), inventoryPerStore: z.number().nullable().optional(), inventoryRevenue: z.number().nullable().optional(), inventoryTurnover: z.number().nullable().optional(), investmentIncome: z.number().nullable().optional(), loanToDeposit: z.number().nullable().optional(), lossRatio: z.number().nullable().optional(), netInterestIncome: z.number().nullable().optional(), netMargin: z.number().nullable().optional(), netProfitMargin: z.number().nullable().optional(), nim: z.number().nullable().optional(), ocfRevenue: z.number().nullable().optional(), operatingMargin: z.number().nullable().optional(), operatingRatio: z.number().nullable().optional(), payoutRatio: z.number().nullable().optional(), ppeIntensity: z.number().nullable().optional(), premiumGrowth: z.number().nullable().optional(), provisionToLoans: z.number().nullable().optional(), reserveRatio: z.number().nullable().optional(), retainage: z.number().nullable().optional(), revenueGrowth: z.number().nullable().optional(), revenuePerStore: z.number().nullable().optional(), roa: z.number().nullable().optional(), roe: z.number().nullable().optional(), sgaRevenue: z.number().nullable().optional(), storeCount: z.number().nullable().optional(), underwritingIncome: z.number().nullable().optional(), warrantyReserve: z.number().nullable().optional(), workingCapitalRatio: z.number().nullable().optional()}); export const keyMetricsIndustry = z.object({billings: z.number().nullable().optional(), capexToDepreciation: z.number().nullable().optional(), capexToRevenue: z.number().nullable().optional(), cashRunwayQuarters: z.number().nullable().optional(), daToRevenue: z.number().nullable().optional(), debtToEbitda: z.number().nullable().optional(), debtToEquity: z.number().nullable().optional(), deferredRevenue: z.number().nullable().optional(), deferredRevenueGrowth: z.number().nullable().optional(), dividendPayout: z.number().nullable().optional(), ebitdaMargin: z.number().nullable().optional(), fcfConversion: z.number().nullable().optional(), fcfMargin: z.number().nullable().optional(), grossMargin: z.number().nullable().optional(), interestCoverage: z.number().nullable().optional(), inventoryTurnover: z.number().nullable().optional(), rdToRevenue: z.number().nullable().optional(), remainingPerformanceObligations: z.number().nullable().optional(), sbcToRevenue: z.number().nullable().optional(), sgaToRevenue: z.number().nullable().optional()}); export const getApiApiFinancialsKeyMetricsResponse = z.array(z.object({period: z.string(), fiscalYear: z.number().int(), fiscalPeriod: fiscalPeriod, sector: z.string().nullable(), sectorId: z.string().nullable(), industry: z.string().nullable(), industryId: z.string().nullable(), general: keyMetricsGeneral, sectorMetrics: keyMetricsSector, industryMetrics: keyMetricsIndustry})); 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 }; /** * Sector-aware key metrics * * GET /api/financials/key-metrics */ export const getApiApiFinancialsKeyMetrics = async (args: GetApiApiFinancialsKeyMetricsArgs, options?: GetApiApiFinancialsKeyMetricsOptions) => { const {baseUrl = "https://api.stockfit.io/v1", fetch: fetchFn = fetch, bearerAuth} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.symbol != null) query.append("symbol", String(args.symbol)) if (args.cik != null) query.append("cik", String(args.cik)) if (args.cusip != null) query.append("cusip", String(args.cusip)) if (args.composite_figi != null) query.append("composite_figi", String(args.composite_figi)) if (args.share_class_figi != null) query.append("share_class_figi", String(args.share_class_figi)) if (args.currency != null) query.append("currency", String(args.currency)) if (args.period != null) query.append("period", String(args.period)) if (args.limit != null) query.append("limit", String(args.limit)) if (bearerAuth !== undefined) headers.set('authorization', `Bearer ${bearerAuth}`) const url = `${baseUrl.replace(/\/+$/, '')}/api/financials/key-metrics${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(getApiApiFinancialsKeyMetricsResponse, await res.json()) };