import { defineSource } from '@jaspers-ai/sdk' import { z } from 'zod' import { HOSTS, baseField, get, iso, type Params } from './client' // Technical indicators Massive computes: SMA, EMA, RSI, MACD, newest first. export const indicator = defineSource({ description: 'A technical indicator for any ticker, newest first: time (ISO) and value; MACD adds signal and histogram. SMA, EMA, RSI, or MACD over day/hour/minute bars.', hosts: HOSTS, input: z.object({ ticker: z.string().min(1).describe('A ticker of any asset class, like AAPL or X:BTCUSD.'), indicator: z.enum(['sma', 'ema', 'rsi', 'macd']).default('sma').describe('sma, ema, rsi, or macd.'), timespan: z.enum(['minute', 'hour', 'day', 'week', 'month', 'quarter', 'year']).default('day').describe('The bar size.'), window: z.number().int().min(1).default(14).describe('The window for sma, ema, rsi, like 50 for a 50-day SMA.'), short_window: z.number().int().min(1).default(12).describe('MACD short EMA window.'), long_window: z.number().int().min(1).default(26).describe('MACD long EMA window.'), signal_window: z.number().int().min(1).default(9).describe('MACD signal window.'), series_type: z.enum(['open', 'high', 'low', 'close']).default('close').describe('Which price to compute on.'), from: z.string().optional().describe('Earliest date, YYYY-MM-DD.'), to: z.string().optional().describe('Latest date, YYYY-MM-DD.'), adjusted: z.boolean().default(true).describe('Adjust for splits.'), limit: z.number().int().min(1).max(5000).default(250).describe('Most values to answer.'), base: baseField, }), async run(args, ctx) { const t = args.ticker.trim().toUpperCase() const windows: Params = args.indicator === 'macd' ? { short_window: args.short_window, long_window: args.long_window, signal_window: args.signal_window } : { window: args.window } const body = await get(ctx, args.base, `/v1/indicators/${args.indicator}/${t}`, { timespan: args.timespan, series_type: args.series_type, adjusted: args.adjusted, 'timestamp.gte': args.from, 'timestamp.lte': args.to, order: 'desc', limit: args.limit, ...windows, }) const values = (body?.results?.values ?? []) as any[] return values.map((v) => ({ ticker: t, indicator: args.indicator, time: iso(v.timestamp), value: v.value ?? null, ...(args.indicator === 'macd' ? { signal: v.signal ?? null, histogram: v.histogram ?? null } : {}), })) }, })