// Thin typed client over the tRPC backend (DESIGN.md §2). Slice 1 uses a fetch wrapper // rather than @trpc/client to keep the Next app decoupled from the server's Node-native // type graph; the server is a real tRPC server and full end-to-end client types can be // layered later without changing this surface. import type { OptionChainRow, OptionGreeks } from "@/types/options"; export interface Quote { symbol: string; price: number; bid?: number | null; ask?: number | null; change?: number | null; changePercent?: number | null; iv?: number | null; /** Session of the displayed last print. */ session?: 'regular' | 'pre' | 'post' | null; /** RTH last when price is an extended print. */ regularPrice?: number | null; } export interface PriceCandle { ts: string; o: number; h: number; l: number; c: number; v: number; adjClose?: number | null; } export interface SymbolMeta { symbol: string; name?: string | null; sector?: string | null; industry?: string | null; exchange?: string | null; tickerKind: string; description?: string | null; website?: string | null; marketCap?: number | null; currency?: string | null; employees?: number | null; country?: string | null; city?: string | null; peers?: string[] | null; } export interface SymbolSearchResult { symbol: string; name: string | null; sector: string | null; industry: string | null; exchange: string | null; ticker_kind: string; cik: string | null; } export interface FundHoldersResult { symbol: string; tracked: Array<{ fundId: string; fundName: string; shares: number | null; valueUsd: number | null; asOf: string; source: string; notes: string | null; weightPct: number | null; }>; institutionCount: number; } export interface XPost { post_id: string; author_handle: string; body_text: string | null; posted_at: string; engagement: number; cashtag?: string | null; attribution?: string | null; } export interface XAccountRow { id: string; symbol: string; handle: string; label: string; created_at: string; pullStatus?: string | null; pullAt?: string | null; pullError?: string | null; lastPostAt?: string | null; } export interface FundFreshness { xHandle: string | null; xPull: { status: "pending" | "in_flight" | "done" | "backoff" | "failed" | "none"; lastAttempt: string | null; error: string | null; }; lastPostAt: string | null; lastCaptureAt: string | null; last13fAt: string | null; } // ─── Dealer Flow types ───────────────────────────────────────────────────── export interface DealerMapCell { strike: number; expiry: string; gex: number; vex: number; oi: number; volume: number; polarity: "pos" | "neg" | "near_zero"; isKing: boolean; } export interface DealerMapLevels { king: { strike: number; expiry: string; gex: number } | null; gatekeepers: Array<{ strike: number; absGex: number }>; flip: number | null; callWall: number | null; putWall: number | null; airPockets: Array<{ from: number; to: number }>; clusters: Array<{ strikes: number[]; absGexSum: number }>; } export interface DealerMapEducator { headline: string; regime: string; levels: string[]; scenario: string; patterns: string[]; questions: string[]; caveats: string[]; footer: string; } export interface DealerMapPayload { symbol: string; spot: number; asOf: string; quoteAsOf?: string | null; chainAsOf?: string | null; delayNote: string; delaySeconds: number | null; oiFreshness: string; providerId: string; convention: string; unit: string; computation: string; expiries: string[]; cells: DealerMapCell[]; byStrike: Array<{ strike: number; gex: number; vex: number; absGex: number }>; netVexByStrike: Array<{ strike: number; vex: number }>; levels: DealerMapLevels; regimeAtSpot: "positive" | "negative" | "mixed"; scenario: string; patterns: Array<{ id: string; confidence?: number; [k: string]: unknown }>; velocity?: { asOfPrev: string; nodes: Array<{ strike: number; growing: boolean; deltaAbsGex: number }>; growingNearAirPocket: boolean; } | null; opexHints: { isNearOpex: boolean; note: string }; coveragePct: number; bsmFillPct: number; quality?: { greekQuality: "ok" | "degraded" | "poor"; oiEligible: number; included: number; skippedBadIv: number; bsmFills: number; fallbackIvUses: number; bsmFillPct: number; badIvSkipPct: number; fallbackIvPct: number; notes: string[]; }; } export interface DealerIntegrityReport { status: "complete" | "degraded" | "incomplete"; checks: Array<{ id: string; level: "hard" | "soft"; ok: boolean; detail: string }>; hardFailed: string[]; softFailed: string[]; metrics: { expiries: number; oiEligible: number; included: number; cells: number; byStrike: number; nearSpotOiStrikes: number; minOiStrikesPerExpiry: number; includePct: number; badIvSkipPct: number; bsmFillPct: number; }; } /** Han-style day script under the active GEX convention. */ export interface HanStyleLevels { symbol: string; spot: number; asOf: string; convention: string; script: string; chips: string[]; longSupport: number | null; shortKing: number | null; resistance: number | null; opexPath: number[]; regimeLabel: "long_gamma" | "short_gamma" | "mixed"; } export interface DealerMapGetResult { map: DealerMapPayload | null; educator: DealerMapEducator | null; /** Support / resist / short king / OPEX path (updates with Classic vs Dealer toggle). */ hanScript?: HanStyleLevels | null; status: "ok" | "stale" | "pending" | "empty" | "incomplete"; isStale: boolean; scheduled: boolean; providerId: string; message?: string; integrity?: DealerIntegrityReport; } export interface DealerMapLevelsResult { symbol: string; status: string; scheduled: boolean; levels: DealerMapLevels | null; spot: number | null; delayNote: string | null; providerId: string; regimeAtSpot?: string; scenario?: string; patterns?: string[]; } export interface DealerMapScenarioResult { symbol: string; status: string; scenario: string; regimeAtSpot: string; patterns: Array<{ id: string; confidence?: number }>; educator: DealerMapEducator | null; delayNote: string | null; providerId: string; } export interface DealerMapVelocityResult { symbol: string; status: string; velocity: DealerMapPayload["velocity"]; message: string | null; } export interface DealerMapExplainResult { ok: boolean; source: "llm" | "layer0_fallback" | "rate_limited" | "no_endpoint" | "error"; headline: string; sections: { whatMapShows: string; keyLevels: string[]; scenarioInPlainEnglish: string; questionsToAsk: string[]; caveats: string[]; dataFreshness: string; }; model?: string; cached?: boolean; error?: string; } /** Study Desk practice setups. */ export interface DealerStudyProposal { hypothesisId: string; title: string; bias: "educational_long" | "educational_short" | "educational_range"; mapReading: string; studyQuestion: string; entry: number; invalidation: number; target: number; horizonDays: number; confidence: number; disclaimer: string; histWinRate?: number | null; histN?: number; histAvgScore?: number | null; mentorWinRate?: number | null; mentorN?: number; mentorClaimType?: string | null; rankScore?: number; } export interface DealerStudyRow { id: string; symbol: string; hypothesis_id: string; title: string; bias: string; map_reading: string; study_question: string; entry: number; invalidation: number; target: number; horizon_days: number; confidence: number | null; status: string; grade: string | null; score: number | null; grade_summary: string | null; grade_path: string | null; graded_at: string | null; logged_at: string; disclaimer: string; } export interface DealerStudyScorecardRow { hypothesisId: string; n: number; wins: number; losses: number; expired: number; incomplete: number; winRate: number | null; avgScore: number | null; } export interface MentorCallRow { id: string; handle: string; post_id: string; posted_at: string | null; symbol: string | null; bias: string; claim_type: string; entry: number; invalidation: number; target: number; horizon_days: number; excerpt: string | null; extract_confidence: number | null; status: string; grade: string | null; score: number | null; grade_summary: string | null; logged_at: string; } export interface UserLlmStatus { configured: boolean; baseUrl: string | null; model: string | null; keyHint: string | null; updatedAt: string | null; } export interface XCredentialsStatus { configured: boolean; healthy: 'healthy' | 'degraded'; lastError: string | null; updatedAt: string | null; } export interface SectorRotationItem { symbol: string; name: string; /** Legacy: inflow ≈ leading, outflow ≈ lagging (RS vs SPY, not abs flow). */ group: 'outflow' | 'inflow'; subGroup: string; kind?: 'sector' | 'style' | 'region' | 'thematic' | 'custom' | 'auto'; /** True when this ETF is a sector/theme proxy for at least one open portfolio holding. */ portfolioLinked?: boolean; /** Holding symbols that map into this ETF (Auto filter provenance). */ portfolioHoldings?: string[]; leadership?: 'leading' | 'lagging' | 'inline' | 'unknown'; earlyWatch?: boolean; rank1M?: number | null; rank1W?: number | null; relVol?: number | null; oneWeek: number | null; oneMonth: number | null; threeMonth: number | null; sixMonth: number | null; oneYear: number | null; oneDay?: number | null; rsOneWeek?: number | null; rsOneMonth?: number | null; rsThreeMonth?: number | null; rsSixMonth?: number | null; rsOneYear?: number | null; rsOneDay?: number | null; } export interface AlertEventRow { id: string; userId: string; type: string; severity: string; title: string; description: string; symbol?: string; createdAt: string; acknowledged: boolean; dedupKey: string; payload: Record; } export interface AlertSubscriptionRow { id: string; owner_id: string; watchlist_id: string | null; symbol: string | null; alert_type: string; enabled: boolean; params: string; last_fired: string | null; created_at: string; } export interface AlertTypeToggleRow { type: string; label: string; description: string; enabled: boolean; } export interface ProducerRunStatusRow { producer: string; frequency: string; lastRunAt: string | null; lastEventsFired: number; lastDurationMs: number; lastError: string | null; } export interface AlertStatusRow { producers: ProducerRunStatusRow[]; subscriptionsByType: Array<{ alertType: string; enabledCount: number }>; freshness: { vixLevelAgeSec: number | null; latestScFilingAt: string | null; latestInsiderAt: string | null; oldestQuoteAgeSec: number | null; }; } export interface SmtpConfigRow { host: string; port: number; secure: boolean; user: string | null; passEnc: string | null; fromName: string; fromEmail: string; enabled: boolean; } export interface SmtpConfigUpdate { host?: string; port?: number; secure?: boolean; user?: string | null; pass?: string | null; fromName?: string; fromEmail?: string; enabled?: boolean; } export interface RotationData { sectors: SectorRotationItem[]; signal: { strength: 'none' | 'weak' | 'moderate' | 'strong'; spread: number; inflowAvg: number; outflowAvg: number; leadingSubGroup: string; laggingSubGroup?: string; inflowConfirmation: string; outflowConfirmation: string; earlyWatchCount?: number; daysSince: number | null; summary?: string; }; benchmark?: string; coveredSymbols?: number; totalSymbols?: number; earlyHistory?: Array<{ date: string; symbol: string; name: string; rs1w: number | null }>; lesson?: string; fetchedAt: string; } export interface MarketCondition { regime: 'trending-up' | 'trending-down' | 'range-bound'; regimePlain?: string; confidence: number; factors: string[]; explanation: string; metrics: { spy1M: number | null; vix: number | null; treasury10Y: number | null; treasury2Y: number | null; curve10y2y: number | null; unemployment: number | null; consumerSentiment?: number | null; context?: Record; }; risks: Array<{ id: string; label: string; severity: 'info' | 'warning' | 'elevated'; detail: string; }>; updatedAt: string; lesson: string; } export interface SeasonalityMonth { month: number; monthName: string; avgReturnPct: number; winRate: number; sampleYears: number; } export interface HorizonReturns { oneWeek: number | null; oneMonth: number | null; threeMonth: number | null; sixMonth: number | null; oneYear: number | null; threeYear: number | null; fiveYear: number | null; } export interface TickerContext { symbol: string; name: string | null; sector: string | null; industry: string | null; tickerKind: string; performance: { symbol: HorizonReturns; vsMarket: HorizonReturns; vsSector: HorizonReturns | null; vsPeers1M: number | null; marketStance1M: "outperforming" | "underperforming" | "inline" | "unknown"; sectorStance1M: "outperforming" | "underperforming" | "inline" | "unknown"; peerStance1M: "outperforming" | "underperforming" | "inline" | "unknown"; }; market: { regime: string | null; confidence: number | null; spyReturns: HorizonReturns; }; sectorContext: { label: string | null; etf: string | null; etfReturns: HorizonReturns | null; leadership: string | null; }; themeContext: { label: string | null; etf: string | null; etfReturns: HorizonReturns | null; }; classification?: { note: string | null; vendorSector: string | null; vendorIndustry: string | null; }; peers: Array<{ symbol: string; name: string | null; oneMonth: number | null; rsVsMarket1M: number | null; }>; summary: { headline: string; market: string; sector: string; peers: string; synthesis: string; }; } export interface SeasonalityData { symbol: string; months: SeasonalityMonth[]; currentMonth: number; currentMonthAvgPct: number | null; currentMonthWinRate: number | null; currentMonthSampleYears: number; halfYear: { winterAvgPct: number | null; summerAvgPct: number | null; whichHalf: 'winter' | 'summer'; }; electionCycle: { year: number; yearInCycle: 1 | 2 | 3 | 4; label: string; }; calendar: { dayOfMonth: number; nearTurnOfMonth: boolean; quarter: 1 | 2 | 3 | 4; nearQuarterEnd: boolean; }; events: Array<{ id: string; title: string; when: string; impact: 'high' | 'medium'; plainWhy: string; }>; hasEnoughHistory: boolean; lesson: string; } export interface RotationAlertCheckResult { signal: string; spread: number; inflowAvg: number; outflowAvg: number; prevSignal: string | null; alertsCreated: number; } export interface SectorHolding { symbol: string; name: string; weight: number; price: number | null; change: number | null; changePercent: number | null; volume: number | null; marketCap: number | null; returns1W: number | null; returns1M: number | null; returns3M: number | null; returns6M: number | null; returns1Y: number | null; } export interface SectorHoldingsResult { symbol: string; holdings: SectorHolding[]; /** composition source: live Yahoo, 24h cache, static fallback, or empty */ source?: "live" | "cache" | "static" | "empty"; } export interface TruckSalesObservation { date: string; value: number; } export interface TruckSalesData { observations: TruckSalesObservation[]; seriesTitle: string; units: string; configured: boolean; error?: string; } export interface ManufacturingPmiData { observations: TruckSalesObservation[]; seriesTitle: string; units: string; configured: boolean; error?: string; } export interface XFedFeed { cashtagPosts: XPost[]; accountPosts: XPost[]; nextCursor: string | null; configured: boolean; health?: { healthy: 'healthy' | 'degraded'; lastError: string | null } | null; lastPullAt?: string | null; queuedHandles?: number; } export interface MacroCommentary { shortTerm: string; longTerm: string; disclaimer: string; } export interface Snapshot { symbol: string; quote: Quote | null; candles: PriceCandle[] | null; sector: SymbolMeta | null; stale: { quote: boolean; candles: boolean; sector: boolean }; observed?: { quote: string | null; candles: string | null; sector: string | null }; } export interface AuthUser { userId: string; email?: string; complexity?: string; riskTolerance?: string; convexityPosture?: string; onboarded?: boolean; modules?: string[]; experienceStage?: string | null; goal?: string | null; horizon?: string | null; density?: string | null; jargonComfort?: string | null; } // Default short timeout for normal cache/UI calls. LLM paths need much longer // (Ornith reasoning models often take 30–120s). const TRPC_TIMEOUT_MS = 8_000; /** OpenAI-compatible explain / test against local reasoning models. */ const TRPC_LLM_TIMEOUT_MS = 180_000; async function parse(res: Response): Promise { if (!res.ok) { const body = await res.text(); // Prefer structured proxy / tRPC error message over raw HTML "Internal Server Error". try { const j = JSON.parse(body) as { error?: { message?: string; data?: { code?: string } }; message?: string; }; const msg = j?.error?.message ?? j?.message ?? (body.trim() || res.statusText); throw new Error(`tRPC ${res.status}: ${msg}`); } catch (e) { if (e instanceof Error && e.message.startsWith("tRPC ")) throw e; const snippet = body.replace(/\s+/g, " ").trim().slice(0, 200); throw new Error(`tRPC ${res.status}: ${snippet || res.statusText}`); } } const json = await res.json(); if (json?.error) { throw new Error(`tRPC error: ${json.error.message ?? json.error.data?.code ?? "unknown"}`); } return json.result.data as T; } async function trpcQuery( procedure: string, input?: unknown, timeoutMs: number = TRPC_TIMEOUT_MS, ): Promise { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { const search = input ? `?input=${encodeURIComponent(JSON.stringify(input))}` : ""; const res = await fetch(`/api/trpc/${procedure}${search}`, { headers: { "content-type": "application/json" }, credentials: "include", signal: controller.signal, }); return parse(res); } catch (e) { if (e instanceof DOMException && e.name === "AbortError") { throw new Error(`Request timed out (${Math.round(timeoutMs / 1000)}s)`); } throw e; } finally { clearTimeout(timer); } } async function trpcMutate( procedure: string, input: unknown, timeoutMs: number = TRPC_TIMEOUT_MS, ): Promise { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { const res = await fetch(`/api/trpc/${procedure}`, { method: "POST", headers: { "content-type": "application/json" }, credentials: "include", body: JSON.stringify(input), signal: controller.signal, }); return parse(res); } catch (e) { if (e instanceof DOMException && e.name === "AbortError") { throw new Error(`Request timed out (${Math.round(timeoutMs / 1000)}s)`); } throw e; } finally { clearTimeout(timer); } } export type Complexity = "beginner" | "intermediate" | "advanced"; export interface StarterEntry { symbol: string; tickerKind: string; reason: string; } export interface PortfolioEntry { symbol: string; qty: number; avgCost: number; acquiredAt: string; } export interface IndicatorPeriods { ema: number[]; rsi: number; relvol: number; } export interface IndicatorOptions { periods?: IndicatorPeriods; timeframe?: '1m' | '5m' | '1d' | '1wk' | '1mo'; } export interface IndicatorResult { ema?: Record; rsi?: (number | null)[]; relvol?: (number | null)[]; macd?: { macdLine: (number | null)[]; signalLine: (number | null)[]; histogram: (number | null)[] }; } // --- EDGAR / SEC Filings types (Slice 6 / M6) ------------------------------- export interface EdgarFiling { form: string; dateReporter: string; accessionNumber: string; accessionNormalization: string; reportDate: string; reportFile: string; primaryDocument: string; } export interface FilerMeta { cik: string; name: string | null; sic: string | null; } export interface Form13fHolding { cusip: string; issuerName: string; value: number; sshPrnamt: number; } export interface Form13fResult { holdings: Form13fHolding[]; total: number; accession: string; } export interface Form4Transaction { reporter: string; relationship: string; securityTitle: string; transactionDate: string; transactionCode: string; shares: number; price: number; } export interface Form4Result { transactions: Form4Transaction[]; accession: string; } export interface FilingsSearchResult { filings: Array>; } export const api = { market: { snapshot: (symbol: string) => trpcQuery("market.snapshot", { symbol }), /** Batched snapshot for multiple symbols — reduces N+1 API calls */ snapshots: (symbols: string[]) => trpcQuery>("market.snapshots", { symbols }), tickerContext: (symbol: string) => trpcQuery("market.tickerContext", { symbol }), candles: async (symbol: string, timeframe = "1d") => trpcQuery<{ symbol: string; timeframe: string; candles: PriceCandle[]; isStale: boolean; fetchedAt: string | null }>("market.candles", { symbol, timeframe }).then((r) => r?.candles ?? []), candlesWithMeta: (symbol: string, timeframe = "1d") => trpcQuery<{ symbol: string; timeframe: string; candles: PriceCandle[]; isStale: boolean; fetchedAt: string | null }>("market.candles", { symbol, timeframe }), indicators: (symbol: string, opts: IndicatorOptions) => trpcQuery("market.indicators", { symbol, ...opts }), truckSales: () => trpcQuery("market.truckSales"), manufacturingPmi: () => trpcQuery("market.manufacturingPmi"), condition: () => trpcQuery("market.condition"), rotation: () => trpcQuery("market.rotation"), addCustomEtf: (symbol: string, opts?: { name?: string; group?: string }) => trpcMutate<{ added: boolean }>("market.addCustomEtf", { symbol, ...opts }), removeCustomEtf: (symbol: string) => trpcMutate<{ removed: boolean }>("market.removeCustomEtf", { symbol }), listCustomEtfs: () => trpcQuery>("market.listCustomEtfs"), rotationCheckForAlert: () => trpcMutate("market.rotationCheckForAlert", {}), seasonality: (symbol?: string) => trpcQuery("market.seasonality", symbol ? { symbol } : {}), sectorHoldings: (etfSymbol: string) => trpcQuery("market.sectorHoldings", { etfSymbol }), commentary: () => trpcQuery("macro.commentary"), }, edgar: { filings_index: (input: { cik: string; formTypes?: string[]; dateRange?: { from?: string; to?: string } }) => trpcQuery<{ filings: EdgarFiling[]; cik: string }>("edgar.filings_index", input), company_facts: (cik: string) => trpcQuery<{ facts: Record; cik: string }>("edgar.company_facts", { cik }), filer_cik_meta: (cik: string) => trpcQuery<{ meta: FilerMeta; cik: string }>("edgar.filer_cik_meta", { cik }), full_text_search: (q: string) => trpcQuery("edgar.full_text_search", { q }), form13f_holdings: (cik: string, accession: string, opts?: { limit?: number; offset?: number }) => trpcQuery("edgar.form13f_holdings", { cik, accession, ...(opts?.limit != null ? { limit: opts.limit } : {}), ...(opts?.offset != null ? { offset: opts.offset } : {}), }), form4_tx: (cik: string, accession: string) => trpcQuery<{ transactions: Form4Result }>("edgar.form4_tx", { cik, accession }), }, funds: { list: () => trpcQuery>("funds.list", {}), get: (input: { id: string }) => trpcQuery<{ id: string; ci_key: string; fund_name: string; manager_name: string; x_handle: string | null; paywall_status: string; enabled: boolean; freshness: FundFreshness | null; } | null>("funds.get", input), liveBook: (input: { fundId: string }) => trpcQuery>("funds.liveBook", input), adminCreate: (input: { ci_key: string; fund_name: string; manager_name: string; x_handle?: string; paywall_status?: "unknown" | "open" | "paywalled"; enabled?: boolean; }) => trpcMutate<{ id: string; ci_key: string; fund_name: string; manager_name: string; x_handle: string | null; paywall_status: string; enabled: boolean }>("funds.adminCreate", input), adminDelete: (input: { id: string }) => trpcMutate<{ deleted: boolean }>("funds.adminDelete", input), adminSetEnabled: (input: { id: string; enabled: boolean }) => trpcMutate<{ updated: boolean }>("funds.adminSetEnabled", input), adminSync13f: (input: { fundId: string }) => trpcMutate<{ fetched_filings: number; holdings_seen: number; symbols_upserted: number; records_inserted: number; unresolved: Array<{ cusip: string; issuerName: string }>; }>("funds.adminSync13f", input), adminIngestCaptures: (input: { fundId: string }) => trpcMutate<{ fundId: string; postsScanned: number; captures: number; inserted: number; refreshed: number; claims: number; skipped: number; disabled: boolean; }>("funds.adminIngestCaptures", input), }, symbols: { search: (input: { q: string; limit?: number }) => trpcQuery<{ q: string; results: Array<{ symbol: string; name: string | null; sector: string | null; industry: string | null; exchange: string | null; ticker_kind: string; cik: string | null; }>; }>("symbols.search", input), holders: (input: { symbol: string }) => trpcQuery<{ symbol: string; tracked: Array<{ fundId: string; fundName: string; shares: number | null; valueUsd: number | null; asOf: string; source: string; notes: string | null; weightPct: number | null; }>; institutionCount: number; }>("symbols.holders", input), }, mirror: { diff: (input: { fundId: string; base?: number; locked?: boolean; minPositionUsd?: number; minWeightPct?: number; tickSize?: number; }) => trpcQuery<{ base: number; locked: boolean; book_value: number; placed_value: number; rows: Array<{ symbol: string; weight: number; fund_avg_cost: number | null; target_value: number; target_qty: number | null; actual_qty: number; actual_value: number; delta_qty: number; delta_value: number; message: string; excluded: boolean; }>; excluded: Array<{ symbol: string; weight: number; fund_avg_cost: number | null; target_value: number; target_qty: number | null; actual_qty: number; actual_value: number; delta_qty: number; delta_value: number; message: string; excluded: boolean }>; excluded_tail: { count: number; value_usd: number } | null; }>("mirror.diff", input), }, sectorCrosslink: { confirm: (input: { strategyId: string; sector: string }) => trpcQuery<{ strategyId: string; sector: string; matchedSymbols: Array<{ symbol: string; convictionStrength: number; conditionsFired: string[]; oneTapWorkbenchLink: string; }>; rotationSignal: { fromSector: string; toSector: string; confidence: number; date: string } | null; sectorConfirmationReason: string; educationalDisclaimer: string; }>("sectorCrosslink.confirm", input), }, reddit: { subreddit: (input: { subreddit: string; limit?: number }) => trpcQuery<{ posts: Array<{ post_id: string; author_handle: string; subreddit: string; body_text: string | null; posted_at: string; engagement: number; sentiment_score?: number | null; attribution?: string | null; }>; isStale: boolean; }>("reddit.subreddit", input), search: (input: { q: string; limit?: number }) => trpcQuery<{ posts: Array<{ post_id: string; author_handle: string; subreddit: string; body_text: string | null; posted_at: string; engagement: number; sentiment_score?: number | null; attribution?: string | null; }>; isStale: boolean; }>("reddit.search", input), }, auth: { signup: (email: string, password: string) => trpcMutate<{ userId: string }>("auth.signup", { email, password }), login: (email: string, password: string, totp?: string) => trpcMutate<{ userId: string }>("auth.login", { email, password, ...(totp ? { totp } : {}) }), logout: () => trpcMutate<{ ok: boolean }>("auth.logout", {}), changePassword: (current: string, next: string) => trpcMutate<{ ok: boolean }>("auth.changePassword", { current, next }), me: () => trpcQuery("auth.me"), enable2fa: () => trpcMutate<{ totpSecret: string; qrUrl: string; backupCodes: string[] }>("auth.enable2fa", {}), confirm2fa: (totp: string) => trpcMutate<{ ok: boolean }>("auth.confirm2fa", { totp }), oauthStart: (provider: "github" | "google", redirectUri: string) => trpcMutate<{ redirectUrl: string; state: string }>("auth.oauthStart", { provider, redirectUri }), }, onboarding: { starter: (input?: { density?: "focused" | "standard" | "full"; experienceStage?: "never_invested" | "some_stocks" | "active_self_directed" }) => trpcQuery<{ watchlist: StarterEntry[]; density: string; disclaimer: string }>("onboarding.starter", input ?? {}), complete: (input: { experienceStage?: "never_invested" | "some_stocks" | "active_self_directed"; goal?: "grow" | "income" | "protect"; horizon?: "short" | "medium" | "long"; density?: "focused" | "standard" | "full"; jargonComfort?: "plain" | "mixed" | "technical"; complexity?: Complexity; drawdownTolerancePct?: number; firstWatchlistSymbols?: string[]; portfolio?: PortfolioEntry[]; }) => trpcMutate<{ ok: boolean; watchlistId: string; density?: string; complexity?: string; experienceStage?: string }>( "onboarding.complete", input, ), updateProfile: (input: { experienceStage?: "never_invested" | "some_stocks" | "active_self_directed"; goal?: "grow" | "income" | "protect"; horizon?: "short" | "medium" | "long"; density?: "focused" | "standard" | "full"; jargonComfort?: "plain" | "mixed" | "technical"; }) => trpcMutate<{ ok: boolean; experienceStage: string; goal: string; horizon: string; density: string; jargonComfort: string; complexity: string; }>("onboarding.updateProfile", input), }, watchlists: { list: () => trpcQuery("watchlists.list"), listByWatchlist: (name: string) => trpcQuery("watchlists.listByWatchlist", { name }), listWatchlists: () => trpcQuery>("watchlists.listWatchlists"), create: (name: string) => trpcMutate<{ id: string; name: string; symbol_count: number; sort_order: number; created_at: string; kind?: string }>("watchlists.create", { name }), delete: (name: string) => trpcMutate<{ deleted: boolean }>("watchlists.delete", { name }), rename: (oldName: string, newName: string) => trpcMutate<{ renamed: boolean }>("watchlists.rename", { oldName, newName }), reorder: (orders: { id: string; sort_order: number }[]) => trpcMutate<{ ok: boolean }>("watchlists.reorder", { orders }), moveSymbol: (symbol: string, fromWatchlist?: string, toWatchlist?: string) => trpcMutate<{ moved: boolean }>("watchlists.moveSymbol", { symbol, ...(fromWatchlist ? { fromWatchlist } : {}), ...(toWatchlist ? { toWatchlist } : {}) }), addSymbol: (symbol: string, watchlistName?: string, notes?: string) => trpcMutate<{ added: boolean }>("watchlists.addSymbol", { symbol, ...(watchlistName ? { watchlistName } : {}), ...(notes ? { notes } : {}) }), removeSymbol: (symbol: string, watchlistName?: string) => trpcMutate<{ removed: boolean }>("watchlists.removeSymbol", { symbol, ...(watchlistName ? { watchlistName } : {}) }), }, options: { chain: (symbol: string, opts?: { expiry?: string }) => trpcQuery<{ symbol: string; expiration: string | null; rows: OptionChainRow[] }>("options.chain", { symbol, ...opts }), greeks: (symbol: string, opts?: { expiry?: string; strike?: number }) => trpcQuery<{ symbol: string; greeks: OptionGreeks | null; strike?: number; right?: "call" | "put"; lastPrice?: number; impliedVolatility?: number }>("options.greeks", { symbol, ...opts }), }, dealerMap: { get: ( symbol: string, opts?: { forceSchedule?: boolean; maxExpiries?: number; convention?: "classic_call_pos_put_neg" | "dealer_inventory"; }, ) => trpcQuery("dealerMap.get", { symbol, ...opts }), levels: (symbol: string) => trpcQuery("dealerMap.levels", { symbol }), scenario: (symbol: string) => trpcQuery("dealerMap.scenario", { symbol }), velocity: (symbol: string) => trpcQuery("dealerMap.velocity", { symbol }), explain: (symbol: string) => trpcMutate( "dealerMap.explain", { symbol }, TRPC_LLM_TIMEOUT_MS, ), }, dealerStudy: { propose: (symbol: string, opts?: { useMentorHistory?: boolean }) => trpcQuery<{ symbol: string; proposals: DealerStudyProposal[]; message: string | null; status: string; useMentorHistory?: boolean; }>("dealerStudy.propose", { symbol, useMentorHistory: opts?.useMentorHistory === true, }), log: (input: DealerStudyProposal & { symbol: string }) => trpcMutate<{ ok: boolean; id: string; loggedAt: string }>("dealerStudy.log", { symbol: input.symbol, hypothesisId: input.hypothesisId, title: input.title, bias: input.bias, mapReading: input.mapReading, studyQuestion: input.studyQuestion, entry: input.entry, invalidation: input.invalidation, target: input.target, horizonDays: input.horizonDays, confidence: input.confidence, disclaimer: input.disclaimer, }), list: (opts?: { symbol?: string; limit?: number }) => trpcQuery("dealerStudy.list", opts ?? {}), grade: (id: string) => trpcMutate<{ ok: boolean; grade: string; score: number; summary: string; pathNotes: string[]; status: string; }>("dealerStudy.grade", { id }), gradeDue: () => trpcMutate<{ ok: boolean; considered: number; graded: number; incomplete: number }>( "dealerStudy.gradeDue", {}, ), scorecard: () => trpcQuery("dealerStudy.scorecard"), promoteToJournal: (studyId: string) => trpcMutate<{ ok: boolean; tradeId: string; message: string }>("dealerStudy.promoteToJournal", { studyId, }), exportCsv: (opts?: { symbol?: string; limit?: number }) => trpcQuery<{ filename: string; csv: string; rowCount: number }>( "dealerStudy.exportCsv", opts ?? {}, ), }, mentorLedger: { importFromHarvest: (opts?: { handle?: string; maxFiles?: number }) => trpcMutate<{ ok: boolean; imported: number; skipped: number; filesSeen?: number; message: string | null; }>("mentorLedger.importFromHarvest", opts ?? {}), list: (opts?: { handle?: string; status?: string; limit?: number }) => trpcQuery("mentorLedger.list", opts ?? {}), confirm: (input: { id: string; entry?: number; invalidation?: number; target?: number; bias?: "educational_long" | "educational_short" | "educational_range"; horizonDays?: number; symbol?: string; }) => trpcMutate<{ ok: boolean }>("mentorLedger.confirm", input), discard: (id: string) => trpcMutate<{ ok: boolean }>("mentorLedger.discard", { id }), grade: (id: string) => trpcMutate<{ ok: boolean; grade: string; score: number; summary: string; status: string; }>("mentorLedger.grade", { id }), gradeDue: () => trpcMutate<{ ok: boolean; considered: number; graded: number; incomplete: number }>( "mentorLedger.gradeDue", {}, ), scorecard: (handle?: string) => trpcQuery< Array<{ handle: string; claimType: string; n: number; wins: number; losses: number; expired: number; incomplete: number; winRate: number | null; avgScore: number | null; }> >("mentorLedger.scorecard", handle ? { handle } : {}), }, userLlm: { status: () => trpcQuery("userLlm.status"), upsertEndpoint: (input: { baseUrl: string; apiKey?: string; model?: string; keepExistingKey?: boolean }) => trpcMutate("userLlm.upsertEndpoint", input), clear: () => trpcMutate<{ ok: boolean }>("userLlm.clear", {}), test: (input?: { baseUrl?: string; apiKey?: string; model?: string }) => trpcMutate<{ ok: true; model: string; sample: string } | { ok: false; error: string }>( "userLlm.test", input ?? {}, TRPC_LLM_TIMEOUT_MS, ), }, portfolio: { holdings: () => trpcQuery("portfolio.holdings"), addHolding: (symbol: string, shares: number, avgCost: number) => trpcMutate<{ created: boolean }>("portfolio.addHolding", { symbol, shares, avgCost }), updateHolding: (symbol: string, opts: { shares?: number; avgCost?: number }) => trpcMutate<{ updated: boolean }>("portfolio.updateHolding", { symbol, ...opts }), removeHolding: (symbol: string) => trpcMutate<{ removed: boolean }>("portfolio.removeHolding", { symbol }), optionLegs: () => trpcQuery("portfolio.optionLegs"), addOptionLeg: (input: AddOptionLegInput) => trpcMutate<{ id: string; legs: PortfolioOptionLeg[] }>("portfolio.addOptionLeg", input), removeOptionLeg: (id: string) => trpcMutate<{ removed: boolean }>("portfolio.removeOptionLeg", { id }), }, sizing: { compute: (input: { symbol: string; tier: "A_STAR" | "A" | "B" | "C"; riskFraction: number; stopPerShare: number; equity: number; complexity?: "beginner" | "intermediate" | "advanced"; regime?: "trending-up" | "trending-down" | "range-bound"; aStarUnlocked?: boolean; macroOverrideReason?: string; }) => trpcQuery("sizing.compute", input), }, risk: { posture: (input: { equity: number; peakEquity?: number; drawdownTolerancePct?: number; regime?: "trending-up" | "trending-down" | "range-bound"; plans?: Array<{ symbol: string; stopPrice?: number; rewardTarget?: number; cluster?: string; }>; }) => trpcQuery("risk.posture", input), }, dashboard: { rollup: () => trpcQuery[]>("dashboard.rollup", {}), }, institutional: { flow: (symbol: string) => trpcQuery<{ symbol: string; flow: Array>; quarters: string[] }>("institutional.flow", { symbol }), insiderStream: (symbol: string, limit = 200) => trpcQuery<{ symbol: string; events: Array>; netShares: number; count: number }>("institutional.insiderStream", { symbol, limit }), ownershipHistory: (symbol: string) => trpcQuery<{ symbol: string; months: Array<{ month: string; total_shares: number; num_filers: number }> }>("institutional.ownershipHistory", { symbol }), buyEvents: (symbol: string) => trpcQuery<{ symbol: string; events: Array<{ filerName: string | null; sharesAdded: number; estimatedPrice: number; date: string }> }>("institutional.buyEvents", { symbol }), analystRatings: (symbol: string) => trpcQuery<{ symbol: string; ratings: Array<{ firm: string; action: string | null; gradeFrom: string | null; gradeTo: string | null; ratingDate: string; targetFrom: number | null; targetTo: number | null }>; consensus: { strongBuy: number; buy: number; hold: number; sell: number; strongSell: number } | null; error?: string }>("institutional.analystRatings", { symbol }), shortInterest: (symbol: string) => trpcQuery<{ symbol: string; sharesShort: number | null; sharesShortPriorMonth: number | null; sharesPercentSharesOut: number | null; shortRatio: number | null; shortPercentOfFloat: number | null; dateShortInterest: string | null; floatShares: number | null; sharesOutstanding: number | null; daysToCover: number | null; settlementDate: string | null; history: Array<{ settlementDate: string; sharesShort: number | null; avgDailyVolume: number | null; daysToCover: number }> | null; finraBiweeklyHistory: Array<{ settlementDate: string; currentShortPosition: number; previousShortPosition: number | null; daysToCover: number | null; changePercent: number | null }>; finraWeekly: Array<{ weekEnding: string; shortVolume: number; totalVolume: number; shortPct: number }>; discrepancyPct: number | null; discrepancyBetween: string | null; sources: { yfinance: { available: boolean; stale: boolean }; nasdaq: { available: boolean; stale: boolean }; finra: { available: boolean; stale: boolean } }; stale: boolean }>("institutional.shortInterest", { symbol }), }, emotionLogger: { add: (input: { tradeExecutionId: string; priceAtEvent?: number; emotion: string; note?: string }) => trpcMutate<{ success: boolean; log: Record }>("emotionLogger.add", input), getByTrade: (tradeExecutionId: string) => trpcQuery<{ logs: Record[] }>("emotionLogger.getByTrade", { tradeExecutionId }), delete: (id: string) => trpcMutate<{ success: boolean }>("emotionLogger.delete", { id }), }, alerts: { list: (limit = 50) => trpcQuery("alerts.list", { limit }), acknowledge: (alertId: string) => trpcMutate<{ ok: boolean }>("alerts.acknowledge", { alertId }), unackedCount: () => trpcQuery<{ count: number }>("alerts.unackedCount"), acknowledgeAll: () => trpcMutate<{ ok: boolean }>("alerts.acknowledgeAll", {}), clearAll: () => trpcMutate<{ ok: boolean }>("alerts.clearAll", {}), createSubscription: (input: { alertType: string; watchlistId?: string; symbol?: string; params?: string }) => trpcMutate("alerts.createSubscription", input), listSubscriptions: (symbol?: string) => trpcQuery("alerts.listSubscriptions", symbol ? { symbol } : {}), updateSubscription: (id: string, updates: { enabled?: boolean; params?: string }) => trpcMutate("alerts.updateSubscription", { id, ...updates }), deleteSubscription: (id: string) => trpcMutate<{ ok: boolean }>("alerts.deleteSubscription", { id }), listTypes: () => trpcQuery("alerts.listTypes"), toggleType: (alertType: string, enabled: boolean) => trpcMutate("alerts.toggleType", { alertType, enabled }), }, x: { cashtag_search: (symbol: string, limit = 30) => trpcQuery<{ posts: XPost[]; configured: boolean }>("x.cashtag_search", { symbol, limit }), feed: (symbol: string, cursor?: string) => trpcQuery("x.feed", { symbol, limit: 10, cursor }), accountsForSymbol: (symbol: string) => trpcQuery>("x.accountsForSymbol", { symbol }), }, admin: { usersList: () => trpcQuery>("admin.usersList"), setUserModules: (userId: string, modules: string[]) => trpcMutate<{ ok: boolean }>("admin.setUserModules", { userId, modules }), disableUser: (userId: string) => trpcMutate<{ ok: boolean }>("admin.disableUser", { userId }), enableUser: (userId: string) => trpcMutate<{ ok: boolean }>("admin.enableUser", { userId }), deleteUser: (userId: string) => trpcMutate<{ ok: boolean }>("admin.deleteUser", { userId }), userSessions: (userId: string) => trpcQuery>("admin.userSessions", { userId }), resetPassword: (email: string, tempPassword: string) => trpcMutate<{ userId: string }>("admin.resetPassword", { email, tempPassword }), queueHealth: () => trpcQuery>("admin.queueHealth"), alertStatus: () => trpcQuery("admin.alertStatus"), resetBackoff: (sourceKind: string) => trpcMutate<{ cleared: number }>("admin.resetBackoff", { sourceKind }), queueSecFetch: (symbol: string) => trpcMutate<{ ok: boolean }>("admin.queueSecFetch", { symbol }), dealerMapIntegrity: (input: { symbol: string; asOf?: string; recompute?: boolean }) => trpcMutate<{ mode: "live" | "replay"; symbol: string; ok: boolean; summary: string; integrityText: string; integrity: DealerIntegrityReport; king: { strike: number; expiry: string; gex: number } | null; spot: number | null; asOf?: string; spotSource?: string; surfaceRowCount?: number; keptPriorMap?: boolean; cells?: number; quality?: unknown; snapshotCompare?: unknown; }>("admin.dealerMapIntegrity", input), queueStatus: () => trpcQuery("admin.queueStatus"), queuePause: () => trpcMutate<{ paused: boolean }>("admin.queuePause", undefined), queueResume: () => trpcMutate<{ paused: boolean }>("admin.queueResume", undefined), queueClearDone: (olderThanHours: number) => trpcMutate<{ cleared: number }>("admin.queueClearDone", { olderThanHours }), queueSchedules: () => trpcQuery("admin.queueSchedules"), queueSetSchedule: (sourceKind: string, intervalMs: number) => trpcMutate<{ ok: boolean }>("admin.queueSetSchedule", { sourceKind, intervalMs }), queueDeleteSchedule: (sourceKind: string) => trpcMutate<{ ok: boolean }>("admin.queueDeleteSchedule", { sourceKind }), queueLogs: (key: string, limit: number) => trpcQuery("admin.queueLogs", { key, limit }), queueRetryJob: (key: string) => trpcMutate<{ ok: boolean }>("admin.queueRetryJob", { key }), queueRetrySource: (sourceKind: string) => trpcMutate<{ cleared: number }>("admin.queueRetrySource", { sourceKind }), clearSourceCooldown: (sourceKind: string) => trpcMutate<{ ok: boolean; sourceKind: string }>("admin.clearSourceCooldown", { sourceKind }), queueSourcePause: (source: string) => trpcMutate<{ ok: boolean; source: string }>("admin.queueSourcePause", { source }), queueSourceResume: (source: string) => trpcMutate<{ ok: boolean; source: string }>("admin.queueSourceResume", { source }), queueSourceStop: (source: string) => trpcMutate<{ ok: boolean; source: string; cleared: number }>("admin.queueSourceStop", { source }), queueSourceStart: (source: string) => trpcMutate<{ ok: boolean; source: string }>("admin.queueSourceStart", { source }), auditLog: (limit = 50, offset = 0, actor?: string, action?: string) => trpcQuery>("admin.auditLog", { limit, offset, ...(actor ? { actor } : {}), ...(action ? { action } : {}) }), queueLint: (symbol: string, kind: 'sec-lint-holders' | 'sec-lint-insiders') => trpcMutate("admin.queueLint", { symbol, kind }), queueLintAll: (input: { kind: 'sec-lint-holders' | 'sec-lint-insiders' }) => trpcMutate<{ total: number; enqueued: number }>("admin.queueLintAll", input), xCredentialsStatus: () => trpcQuery("admin.xCredentialsStatus"), xCredentialsSet: (ct0: string, auth_token: string) => trpcMutate<{ ok: boolean }>("admin.xCredentialsSet", { ct0, auth_token }), fredKeyStatus: () => trpcQuery<{ configured: boolean }>("admin.fredKeyStatus"), fredKeySet: (apiKey: string) => trpcMutate<{ ok: boolean }>("admin.fredKeySet", { apiKey }), xAccountsList: () => trpcQuery("admin.xAccountsList"), xAccountAdd: (symbol: string, handle: string, label?: string) => trpcMutate<{ id: string }>("admin.xAccountAdd", { symbol, handle, ...(label ? { label } : {}) }), xAccountRemove: (id: string) => trpcMutate<{ ok: boolean }>("admin.xAccountRemove", { id }), xPrune: (olderThanDays?: number) => trpcMutate<{ deleted: number }>("admin.xPrune", { olderThanDays: olderThanDays ?? 30 }), pendingUsers: () => trpcQuery>("admin.pendingUsers"), approveUser: (userId: string) => trpcMutate<{ ok: boolean }>("admin.approveUser", { userId }), rejectUser: (userId: string) => trpcMutate<{ ok: boolean }>("admin.rejectUser", { userId }), dataQualityList: (input?: { symbol?: string; kind?: 'institution_filings' | 'insider_transactions' }) => trpcQuery("admin.dataQualityList", input ?? {}), serverRestart: (target: 'backend' | 'frontend' | 'all' = 'all') => trpcMutate<{ ok: boolean; backend: string; frontend: string }>("admin.serverRestart", { target }), smtpConfig: () => trpcQuery("admin.smtpConfig"), smtpConfigUpdate: (input: SmtpConfigUpdate) => trpcMutate<{ ok: boolean }>("admin.smtpConfigUpdate", input), smtpConfigTest: () => trpcMutate<{ ok: boolean }>("admin.smtpConfigTest", {}), }, strategies: { list: () => trpcQuery>("strategies.list"), create: (input: { name: string; components?: unknown[] }) => trpcMutate<{ id: string }>("strategies.create", input), get: (id: string) => trpcQuery>("strategies.get", { id }), listPresets: () => trpcQuery("strategies.listPresets"), getPreset: (id: string) => trpcQuery("strategies.getPreset", { id }), forkPreset: (presetId: string) => trpcMutate<{ id: string; name: string }>("strategies.forkPreset", { presetId }), suggestTickers: (presetId: string) => trpcQuery("strategies.suggestTickers", { presetId }), }, backtest: { run: (input: { strategyId: string; symbol: string; timeframe?: string }) => trpcQuery<{ equityCurve: Array<{ ts: string; equity: number }>; exitReasons: string[]; sampleSize: number }>("backtest.run", input), evaluateLatest: (input: { strategyId: string; symbol: string }) => trpcQuery<{ fires: boolean; conditions: Record }>("backtest.evaluateLatest", input), runPortfolio: (input: { allocations: Array<{ symbol: string; targetPct: number }>; rebalanceFreq?: string; driftThreshold?: number }) => trpcQuery("backtest.runPortfolio", input), }, screener: { filter: (input: { expression: string; scope?: "watchlist" | "sector"; sector?: string }) => trpcQuery<{ results: Array<{ symbol: string; matched: boolean; why?: string[] } | string>; scope: string }>("screener.filter", input), strategy: (input: { strategyId: string }) => trpcQuery<{ results: Array<{ symbol: string; matched: boolean; why?: string[] } | string> }>("screener.strategy", input), }, reports: { generate: (input: { scope: "symbol" | "watchlist" | "portfolio" | "rotation" | "sizing_year" | "risk_posture"; symbol?: string; data?: Record; }) => trpcQuery<{ html?: string; markdown?: string; title?: string; [k: string]: unknown }>("reports.generate", input), }, derisking: { suggest: (input: { symbol: string; currentPrice: number; avgCost: number; shares: number; ema21?: number; ema50?: number; thesisStatus?: string; currentRegime?: string; portfolioCorrelation?: number; profitTargets?: number[]; dividendYield?: number; payoutRatio?: number }) => trpcQuery<{ suggestions: Array<{ strategy: { id: string; name: string; description: string; alfredFraming: string; requiresOptionsSleeve: boolean }; applicable: boolean; reason: string; details: string }> }>("derisking.suggest", input), dividendHealth: (symbol: string) => trpcQuery<{ data: Record | null }>("derisking.dividendHealth", { symbol }), }, thesisMonitor: { assess: (input: { thesis: string; events: Array<{ type: string; date: string; detail?: string }> }) => trpcQuery<{ status: string; confidence: number; reasoning: string[] }>("thesisMonitor.assess", input), }, trades: { list: () => trpcQuery>>("trades.list"), get: (id: string) => trpcQuery>("trades.get", { id }), save: (input: { id: string; symbol: string; date: string; status: string; tier: string; environment?: string; strategyId?: string; confluenceScore?: number; riskPct?: number; positionSize?: number; entryPrice?: number; stopLoss?: number; targets?: string; justifications?: string; checklistCompleted?: number; allocationPlanId?: string; allocationRole?: string; tradeId?: string; }) => trpcMutate<{ ok: boolean; id: string }>("trades.save", input), close: (id: string, realizedPnl: number, reflection?: string) => trpcMutate<{ ok: boolean }>("trades.close", { id, realizedPnl, ...(reflection ? { reflection } : {}) }), stats: () => trpcQuery<{ totalClosed: number; profitable: number }>("trades.stats"), }, theses: { list: () => trpcQuery>("theses.list"), get: (id: string) => trpcQuery>("theses.get", { id }), create: (input: { symbol: string; statement: string; invalidationCriteria?: string[]; status?: "intact" | "weakening" | "broken"; }) => trpcMutate<{ id: string; symbol: string }>("theses.create", input), update: (input: { id: string; statement?: string; invalidationCriteria?: string[]; status?: "intact" | "weakening" | "broken"; }) => trpcMutate<{ ok: boolean }>("theses.update", input), delete: (id: string) => trpcMutate<{ ok: boolean }>("theses.delete", { id }), }, macro: { commentary: () => trpcQuery<{ shortTerm: string; longTerm: string; disclaimer: string }>("macro.commentary"), regimeClassify: () => trpcQuery<{ regime: string; confidence?: number; factors?: unknown }>("macro.regimeClassify"), }, confluence: { slots: () => trpcQuery<{ slots: Array<{ id: string; name: string; family: string; body: string; granularity: string; explain: string }>; slotIds: string[]; families: string[]; }>("confluence.slots", {}), racks: () => trpcQuery<{ system: Array<{ id: string; ownerId: string | null; name: string; slotIds: string[]; description: string | null; isSystem: boolean; createdAt: string }>; user: Array<{ id: string; ownerId: string | null; name: string; slotIds: string[]; description: string | null; isSystem: boolean; createdAt: string }>; }>("confluence.racks", {}), evaluation: (symbol: string, opts?: { rackId?: string; limit?: number }) => trpcQuery<{ rackId: string | null; symbol: string; latest: Record | null; history: Array>; change: { changed: boolean; changeType: string; current: string; previous: string | null; netEvidenceShift: number; flippedSlotIds: string[]; } | null; }>("confluence.evaluation", { symbol, ...(opts?.rackId ? { rackId: opts.rackId } : {}), ...(opts?.limit ? { limit: opts.limit } : {}) }), backtest: (symbol: string, opts?: { weeks?: number }) => trpcQuery<{ symbol: string; fires: number; horizonDays: number; slots: Array<{ slotId: string; name: string; body: string; direction: string; fires: number; resolvedFires: number; confirmedFires: number; falseAlarms: number; hitRate: number | null; reliabilityWeight: number; }>; sampleSizeCaveat: string; }>("confluence.backtest", { symbol, ...(opts?.weeks ? { weeks: opts.weeks } : {}) }), scorecard: (symbol?: string) => trpcQuery<{ slots: unknown[]; note: string }>("confluence.scorecard", symbol ? { symbol } : {}), signalHistory: (symbol: string) => trpcQuery<{ fires: Array<{ slotId: string; slotName: string; body: 'bull' | 'bear' | 'exit'; firedAt: string; qualityAtFire: string; verdict: 'real' | 'false_alarm' | null; priceConfirmed: boolean; }>; }>("confluence.signalHistory", { symbol }), recentZones: (symbol: string, opts?: { rackId?: string; entries?: number; exits?: number }) => trpcQuery<{ symbol: string; rackId: string | null; entries: Array<{ kind: 'entry' | 'exit'; startAsOf: string; endAsOf: string; open: boolean; qualityAtStart: string; netEvidenceAtStart: number; firedSlotIds: string[]; forwardReturn: number | null; verdict: 'confirmed' | 'false_alarm' | 'open'; }>; exits: Array<{ kind: 'entry' | 'exit'; startAsOf: string; endAsOf: string; open: boolean; qualityAtStart: string; netEvidenceAtStart: number; firedSlotIds: string[]; forwardReturn: number | null; verdict: 'confirmed' | 'false_alarm' | 'open'; }>; entryRule: { label: string; source: string; derivedAt: string | null; sampleCaveat: string; train: { hitRate: number | null; resolved: number; baselineHitRate: number | null } | null; validate: { hitRate: number | null; resolved: number; baselineHitRate: number | null } | null; }; exitRule: { label: string; source: string; derivedAt: string | null; sampleCaveat: string; train: { hitRate: number | null; resolved: number; baselineHitRate: number | null } | null; validate: { hitRate: number | null; resolved: number; baselineHitRate: number | null } | null; }; coverage: { evaluatedDays: number; lookbackDays: number; replayComplete: boolean }; symbolScore: { entry: { zones: number; resolved: number; confirmed: number; hitRate: number | null }; exit: { zones: number; resolved: number; confirmed: number; hitRate: number | null }; }; learningLedger: { ready: boolean; filled: number; total: number }; }>("confluence.recentZones", { symbol, ...(opts?.rackId ? { rackId: opts.rackId } : {}), ...(opts?.entries ? { entries: opts.entries } : {}), ...(opts?.exits ? { exits: opts.exits } : {}), }), saveRack: (input: { id?: string; name: string; description?: string; slotIds: string[] }) => trpcMutate<{ id: string; name: string; slotIds: string[]; isSystem: boolean; description: string | null }>("confluence.saveRack", input), corridorSnapshot: (symbol: string) => trpcQuery<{ symbol: string; snapshot: Record | null; spy: Record | null; series: Array>; corridor: { low: number | null; high: number | null; median: number | null; currentPE: number | null; fairValue1y: number | null; impliedUpside1y: number | null; pePercentile1y: number | null; } | null; }>("confluence.corridorSnapshot", { symbol }), corridorWatchlist: () => trpcQuery<{ symbols: string[] }>("confluence.corridorWatchlist"), corridorWatchlistAdd: (symbol: string) => trpcMutate<{ symbols: string[] }>("confluence.corridorWatchlistAdd", { symbol }), corridorWatchlistRemove: (symbol: string) => trpcMutate<{ symbols: string[] }>("confluence.corridorWatchlistRemove", { symbol }), corridorBacktest: () => trpcQuery<{ rows: Array<{ id: string; articleDate: string; articleId: string; rankingType: string; topSymbols: string[]; bottomSymbols: string[]; topAvgReturn: number | null; bottomAvgReturn: number | null; spread: number | null; isWin: boolean; horizonDays: number; gradedAt: string; }>; aggregate: { entries: number; resolved: number; resolved1y: number; resolved90d: number; hitRate: number | null; avgSpread: number | null; bestSpread: number | null; worstSpread: number | null; avgTopReturn: number | null; avgBottomReturn: number | null; }; }>("confluence.corridorBacktest"), corridorBacktestRun: () => trpcMutate<{ grades: number; resolved: number; aggregate: Record }>("confluence.corridorBacktestRun", {}), runEvaluationNow: (opts?: { symbol?: string }) => trpcMutate>("confluence.runEvaluationNow", opts ?? {}), }, }; export interface TickerSuggestion { symbol: string; name: string | null; role: string; allocationPct: number; reason: string; source: 'live' | 'default'; } export interface StrategyPreset { id: string; name: string; category: string; complexity: string; scope: string; description: string; entry_rules: string; exit_profile: string; risk_policy: string; instrument_type: string; portfolio_template: string | null; conviction_tier_default: string | null; requires_options_unlock: number; forkable: number; example_tickers: string | null; created_at: string; } export interface PortfolioBacktestResult { equityCurve: Array<{ date: string; value: number }>; totalReturn: number; cagr: number; maxDrawdown: number; positions: Array<{ symbol: string; targetPct: number; totalReturn: number; finalValue: number }>; rebalanceEvents: Array<{ date: string; type: string }>; correlationMatrix: Record>; sampleSize: number; } export interface LintResult { symbol: string; kind: string; discoveredCount: number; storedCount: number; missingCount: number; backfilled: number; stale: number; status: 'ok' | 'gaps_found' | 'stale' | 'error'; detail: Record; } export interface DataQualityRow { symbol: string; kind: string; last_checked_at: string; stored_count: number; discovered_count: number; missing_count: number; stale: number; status: string; detail: string; } export interface WatchlistEntry { symbol: string; added_at: string; notes?: string | null; } export interface PortfolioHolding { symbol: string; shares: number; avg_cost: number; added_at: string; } export type OptionRight = "call" | "put"; export type OptionSide = "long" | "short"; export type OptionRole = | "long_call" | "long_put" | "covered_call" | "cash_secured_put" | "other"; export interface PortfolioOptionLeg { id: string; underlying: string; right: OptionRight; side: OptionSide; strike: number; expiry: string; contracts: number; premium: number; multiplier: number; role: OptionRole; acquired_at: string; note: string | null; } export interface AddOptionLegInput { underlying: string; right: OptionRight; side: OptionSide; strike: number; expiry: string; contracts: number; premium: number; multiplier?: number; role?: OptionRole; note?: string; } export interface SizingLayer { name: string; value: number; explanation: string; } export interface SizingResult { shares: number; layers: SizingLayer[]; explanations: string[]; blocked: boolean; blockReason: string | null; override: "macro" | "cluster" | null; halted?: boolean; footer?: string; } export interface RiskRecommendation { id: string; tradeOff: string; explanation: string; severity: "warning" | "info"; } export interface RiskPostureResult { asymmetry: number; clusterExposure: Record; clusterCaps?: Record; drawdownVsTolerance: { currentDrawdownPct: number; tolerancePct: number; status: -1 | 0 | 1; }; halted: boolean; recommendedActions: RiskRecommendation[]; halt: { haltedUntil: string; triggeredBy: string; ts: string } | null; holdingsCount: number; optionLegsCount?: number; totalExposureUsd: number; optionRisk?: { premiumAtRiskUsd: number; cashReservedUsd: number; creditReceivedUsd: number; capitalCommittedUsd: number; uncoveredShortCallCount: number; }; footer: string; } export interface SourceCooldown { source: string; until: string | null; remainingMs: number; consecutiveHits: number; active: boolean; } export interface QueueStatus { paused: boolean; queued: number; in_flight: number; failed: number; backoff: number; done: number; backoff_until: string | null; last_error: string | undefined; counts: Record; lastErrors: Array<{ key: string; error: string | null }>; /** Active vendor cool-downs after 429 (ADR-0009). */ sourceCooldowns?: SourceCooldown[]; /** Operator-paused sources (drain frozen, jobs retained). */ pausedSources?: string[]; /** Operator-stopped sources (drain frozen, jobs cleared, enqueue blocked). */ stoppedSources?: string[]; pendingByKind?: Record; demandSize?: number; yahooHealthy?: boolean; secHealthy?: boolean; dataPlaneHealthy?: boolean; dataPlaneNotes?: string[]; } export interface ScheduleEntry { sourceKind: string; intervalMs: number; lastEnqueued: string | null; nextEnqueue: string | null; } export interface ErrorLogEntry { attempt: number; errorMessage: string | null; errorStack: string | null; ts: string; }