/** * Reusable analysis prompts. Each returns a single user message that tells the model which tools to * call and how to structure the answer. */ import { z } from 'zod'; export interface PromptDef = Record> { name: string; title: string; description: string; args: S; build: (args: z.infer>) => string; } function def>(p: PromptDef): PromptDef { return p; } export const termStructureAnalysis = def({ name: 'term-structure-analysis', title: 'Term structure analysis', description: 'Analyse the futures curve of a root (contango/backwardation, spreads, roll yield) on a date, optionally versus an earlier date.', args: { root: z.string().describe('Futures root, e.g. CL, NG, ES'), as_of: z.string().optional().describe('Curve date YYYY-MM-DD (default latest)'), compare_to: z.string().optional().describe('Earlier date to compare the curve with, YYYY-MM-DD'), }, build: ({ root, as_of, compare_to }) => [ `Analyse the ${root.toUpperCase()} futures term structure${as_of ? ` as of ${as_of}` : ' (latest available)'}${compare_to ? ` and compare it with ${compare_to}` : ''}.`, '', 'Steps:', `1. Call get_term_structure(root="${root.toUpperCase()}"${as_of ? `, as_of="${as_of}"` : ''}).${compare_to ? ` Then call it again with as_of="${compare_to}".` : ''}`, '2. Determine whether the curve is in contango (upward sloping) or backwardation (downward sloping); quantify the front-to-second spread in price and in % annualised (spread / front price × 365 / days between expiries).', '3. Note any kinks (seasonality for NG/ZC, delivery-month effects for CL) and where volume/open interest concentrate.', '4. Explain the implied roll yield for a long front-month position and what the shape suggests about inventories / financing.', '5. If the API reports missing prices, say so explicitly — never fill gaps.', '', 'Output: a short table (contract · expiry · price · spread vs front · OI) followed by 4-6 bullet conclusions.', ].join('\n'), }); export const compareContracts = def({ name: 'compare-contracts', title: 'Compare two futures contracts', description: 'Compare two individual futures contracts (e.g. ESZ25 vs ESH26) over a window: performance, spread, volume/OI migration, calendar-spread behaviour.', args: { symbol_a: z.string().describe('First contract symbol, e.g. ESZ25'), symbol_b: z.string().describe('Second contract symbol, e.g. ESH26'), days: z.string().optional().describe('Look-back window in calendar days (default 30)'), }, build: ({ symbol_a, symbol_b, days }) => { const n = Number(days) > 0 ? Number(days) : 30; const a = symbol_a.toUpperCase(); const b = symbol_b.toUpperCase(); return [ `Compare the futures contracts ${a} and ${b} over the last ${n} days.`, '', 'Steps:', `1. Call get_coverage(kind="futures_contract", symbol="${a}") and the same for ${b} to know the available range and any gaps.`, `2. Call get_bars(asset="futures_contract", symbol="${a}", timeframe="1day", start=) and the same for ${b}.`, '3. Align both series on datetime and compute: total return, annualised volatility, the daily calendar spread (B − A) and its trend, and the share of volume/open interest in each contract (roll migration).', '4. Flag whichever contract looks like the front month and estimate the likely roll window from the volume crossover.', '5. Mention explicitly if a day is missing in one leg — do not interpolate.', '', 'Output: a compact comparison table then 3-5 bullets of interpretation. Ask before pulling intraday data (large).', ].join('\n'); }, }); export const fundamentalsSnapshot = def({ name: 'fundamentals-snapshot', title: 'Fundamentals snapshot', description: 'One-page fundamental picture of a US company: latest ratios, last quarters, filings calendar and the caveats of point-in-time data.', args: { ticker: z.string().describe('US ticker, e.g. AAPL'), quarters: z.string().optional().describe('Number of recent quarters to show (default 4)'), }, build: ({ ticker, quarters }) => { const q = Number(quarters) > 0 ? Number(quarters) : 4; const t = ticker.toUpperCase(); return [ `Build a fundamentals snapshot of ${t}.`, '', 'Steps:', `1. get_ratios(ticker="${t}") for the current valuation / profitability / leverage picture.`, `2. get_financial_statements(ticker="${t}", statement="income", period="quarterly", limit=${q}) and the same for "cashflow" (revenue, operating income, net income, operating cash flow, capex → free cash flow).`, `3. get_filings(ticker="${t}", forms=["10-K","10-Q","8-K"], limit=8) to date the numbers and spot recent events.`, `4. Optionally get_ratios_daily(ticker="${t}", metrics=["pe","fcf_yield"], start=<1 year ago>) to place today's valuation in its 1-year range.`, '5. Every figure must cite the period end and the filing date it comes from (point-in-time). Say when a metric is unavailable rather than estimating it.', '', 'Output: headline (price-agnostic) summary, a quarterly table, valuation context, 3 risks/observations drawn only from the data returned.', ].join('\n'); }, }); export const PROMPTS: PromptDef[] = [termStructureAnalysis, compareContracts, fundamentalsSnapshot];