import { definePlugin, defineView } from '@jaspers-ai/sdk' import { z } from 'zod' import { cancelOrder, placeOrder } from './actions' import { optionBars, optionChain, optionLatestQuote, optionLatestTrade, optionSnapshots, optionTrades } from './options' import { stockBars, stockLatestQuote, stockLatestTrade, stockQuotes, stockSnapshots, stockTrades } from './stocks' import { account, assets, orders, positions } from './trading' import { AccountPanel } from './views/AccountPanel' import { Blotter } from './views/Blotter' import { Watchlist } from './views/Watchlist' export default definePlugin({ id: 'alpaca', secrets: { key_id: { label: 'Alpaca API key ID (paper account; also used for market data)' }, secret_key: { label: 'Alpaca API secret key (paper account; also used for market data)' }, live_key_id: { label: 'Alpaca LIVE account API key ID (only for live_account: true)' }, live_secret_key: { label: 'Alpaca LIVE account API secret key (only for live_account: true)' }, }, sources: { 'stock-bars': stockBars, 'stock-quotes': stockQuotes, 'stock-trades': stockTrades, 'stock-latest-quote': stockLatestQuote, 'stock-latest-trade': stockLatestTrade, 'stock-snapshots': stockSnapshots, 'option-bars': optionBars, 'option-trades': optionTrades, 'option-latest-quote': optionLatestQuote, 'option-latest-trade': optionLatestTrade, 'option-snapshots': optionSnapshots, 'option-chain': optionChain, account, positions, orders, assets, 'place-order': placeOrder, 'cancel-order': cancelOrder, }, views: { watchlist: defineView(Watchlist, { title: 'Alpaca watchlist', state: z.object({ symbols: z.array(z.string()).max(100).default(['AAPL', 'MSFT', 'NVDA']).describe('Stock symbols to show.'), feed: z.enum(['iex', 'sip', 'delayed_sip']).default('iex').describe('Data feed.'), }), output: z.object({ symbols: z.array(z.string()), count: z.number(), rows: z.array(z.object({ symbol: z.string(), last: z.number().nullable(), change_pct: z.number().nullable() })), }), instructions: 'Latest Alpaca snapshots (last, bid, ask, change, change %, volume) for state.symbols, refreshed every 15 s. Set state.symbols and state.feed (iex free, sip paid).', renders: [stockSnapshots], summarize: (state, output) => `Alpaca watchlist: ${output.count ?? 0} of ${state.symbols?.length ?? 0} symbols`, }), account: defineView(AccountPanel, { title: 'Alpaca account', state: z.object({ live_account: z.boolean().default(false).describe('Show the LIVE account instead of paper.') }), output: z.object({ mode: z.string(), equity: z.number().nullable(), cash: z.number().nullable(), buying_power: z.number().nullable(), day_pl: z.number().nullable(), positions: z.number(), }), instructions: 'The Alpaca account summary and its positions, paper by default. Set state.live_account to true only when the user asks for the live account.', renders: [account, positions], summarize: (_state, output) => `Alpaca ${output.mode ?? 'paper'}: equity ${output.equity ?? '–'}, ${output.positions ?? 0} positions`, }), orders: defineView(Blotter, { title: 'Alpaca orders', state: z.object({ status: z.enum(['open', 'closed', 'all']).default('all'), limit: z.number().int().min(1).max(500).default(50), live_account: z.boolean().default(false).describe('Show the LIVE account instead of paper.'), }), output: z.object({ status: z.string(), count: z.number(), open: z.number() }), instructions: 'An Alpaca order blotter, paper by default. Set state.status (open|closed|all) and state.limit. Its cancel buttons cancel only on the user\'s click and confirmation.', renders: [orders], summarize: (state, output) => `Alpaca orders (${state.status ?? 'all'}): ${output.count ?? 0}, ${output.open ?? 0} open`, }), }, })