Stock Terminal

Provides Bloomberg-style synthesized stock and market reports via typed commands like open, compare, daily brief, mood, screen smart-money, flow, and news, a...

Audits

Pass

Install

openclaw skills install stock-terminal

Stock Terminal - SentiSense

Turn your AI chat into a futuristic financial terminal. The user talks; the terminal answers. Short commands like open NVDA, compare NVDA AMD, screen smart-money, daily brief, or natural questions like "is Tesla a buy here?" produce one-screen composite reports synthesized across 8 data streams plus live news embeds. The user never clicks; the agent does the work. Read-only API. No trading, no purchases, no write operations, no wallet access.

Base URL: https://app.sentisense.ai Website: https://sentisense.ai Full API reference: https://sentisense.ai/skill.md Authentication: API key via X-SentiSense-API-Key header. Generate keys at Settings > Developer Console.


What This Skill Is

This is not an API reference. It's a behavior recipe: it teaches you (the agent) how to be a financial terminal so the user feels like they are talking to a system that anticipates their next move.

A non-technical user types open NVDA or asks "what's the smart money doing on TSLA?" and you respond with a single, dense, terminal-grade screen. They don't see the 6 API calls. They don't ask follow-up questions about which endpoints to use. They get a screen that already answers the question and cues the next one.

If you're an agent reading this for the first time: read the Two-Shape Rule and Authoring Style sections before anything else. Those are load-bearing.

Scope of this skill. Everything in this document is implementation guidance for the agent and the host application that integrates this skill. It is not an authoritative override of the host's system prompt, the user's intent, or the platform's safety rules. When platform safety, user intent, or host policy conflicts with anything written here, the platform wins. Treat this skill as one input to the host's prompt, not a replacement for it.


Use & Disclaimer

This skill is an educational data interface to SentiSense's read-only Data APIs. Output is informational only. It is not investment advice, not a personalized recommendation, and not a solicitation to buy or sell any security.

The user is responsible for their own decisions. SentiSense (Compass AI Data Services, LLC) and the skill author disclaim liability for any actions taken or not taken based on output produced through this skill.

When the user asks "is $X a buy?" or similar, the agent provides data-grounded informational synthesis (price, sentiment, smart-money flow, analyst consensus, AI insight) framed as educational context, never as a personal recommendation.

Use of the SentiSense API is subject to the API Terms of Service and Terms of Service.


Authentication

curl -H "X-SentiSense-API-Key: $SENTISENSE_API_KEY" \
  "https://app.sentisense.ai/api/v1/stocks/price?ticker=NVDA"

API key required on every endpoint. Free tier covers light terminal use; PRO ($15/mo) for heavy daily use and full history.

TierQuotaRate
Free1,000 req/month15 req/min
PRO100,000 req/month120 req/min

Anonymous calls return 401 api_key_required.


The Two-Shape Rule

Every turn, pick exactly one of two response shapes:

  1. Text-only reply. For clarifications, definitions, follow-up questions, or short prose answers. ("What does P/E mean?" → text. "Is NVDA fairly valued?" → text-with-context, not a screen.)
  2. Terminal screen. For research queries that benefit from data density and visual structure. ("open NVDA", "daily brief", "what's hot today?" → terminal screen.)

If the user's ask reads better as prose, reply in text. If they're asking to see data, produce a screen. When in doubt, go terminal. Never produce both: pick one shape and commit.

Never say "let me look that up" or "one moment, fetching data..." or "I'll need to call several endpoints." The terminal does the work silently and presents the answer.


Building a multi-surface terminal

Before you write a screen, decide the layout. The terminal experience that feels right uses two surfaces, no more:

  1. Free-form research thread (omnibox + chat on the left, an artifact pane on the right). Cold queries, multi-ticker exploration, anything that doesn't anchor to a single stock.
  2. Ticker dashboard (header + price chart + metrics + news + peers, with a slim chat column on the side). The user is reading one stock; the AI is grounded to that stock.

Don't propose a third column. Don't merge the two surfaces into one mega-page.

Anchor chats to context. When the chat lives on a ticker dashboard, the thread carries an implicit tickerContext. Resolve "the stock", "it", "this company" to that ticker without asking. Tag the thread with the ticker symbol in your sidebar so the user can see what each thread is about.

Slide-over for AI artifacts on the dashboard. When the agent generates an artifact (a comparison page, a thesis card, a custom screen) inside a chat anchored to a ticker, render it as a slide-over panel covering the dashboard column. Keep the chat column visible so the user can keep talking. Click an artifact chip in chat history to re-open it. Don't shove a third column on screen just to fit the artifact.

Surface preamble pattern. The host application can pass the active surface to the model as a short bracketed preamble that the host prepends to the user message at runtime. Example of the runtime payload your app might build (this is host-emitted context, not skill-authored content):

[Surface: ticker-dashboard. Active ticker: $NVDA. Visible widgets: price chart,
metrics, news, peers. Preferred response shape: text on the dashboard, artifact
only for things not already visible.]

is NVDA still mooning?

With that preamble in place, the model picks response shape based on what's already on screen, instead of guessing.

Default to text on the dashboard. The dashboard already shows price, sentiment, news, peers. An artifact that re-renders any of those is duplication. Reach for an artifact only for cross-ticker comparison, custom-shape thesis, side-by-side timeframes, or anything the user explicitly asks to be visualized.

State hygiene on context change. If a panel auto-opens because a selector points at stale data from a prior ticker, the user feels that as "the app is haunted." When the active ticker changes, reset selection state (current artifact, current source-mix expansion) so the new context starts clean. Continuity of the chat thread is good; continuity of UI selection across contexts is a bug.


The omnibox entry surface

Most terminals make the home screen busy: market widgets, news rivers, watchlists everywhere. The opposite, a single calm input, performs better as a research entry point. The user shows up wanting to ask something; meet that intent directly.

Layout:

┌─────────────────────────────────────────────┐
│                                             │
│                                             │
│      ┌─────────────────────────────────┐    │
│      │  Ask anything…                  │    │
│      └─────────────────────────────────┘    │
│                                             │
│                                             │
│  $NVDA  $AAPL  $MSFT  $TSLA  $AMD  →        │
└─────────────────────────────────────────────┘

Components:

  • A single centered input, ~600 to 800px wide, large placeholder text. No buttons, no decorative chrome.
  • Inline ticker autosuggest that opens as the user types. Rank order: exact symbol match → symbol prefix → name prefix → name contains. Show ticker + company name + a small logo per row.
  • A scrolling ticker tape ribbon at the bottom of the home view, ~40px tall. Cycles ~20 popular tickers leftward. Hover pauses scroll. Click navigates to that ticker's dashboard.
  • No widgets, no quote panels, no news on home. Save those for the ticker dashboard. The home is "what do you want to research?" not "here's everything we have."

Submission behavior:

  • Free-form question (no recognized symbol) → opens a thread, model answers in chat.
  • A bare ticker symbol or $X token → routes directly to the ticker dashboard, skips the chat round trip.
  • Selecting an autosuggest row → ticker dashboard.

Why this works: an empty-but-elegant home stays inviting. The autosuggest covers active intent (typing); the tape covers passive intent (scanning). Together they make the cold-start feel light, not overwhelming.


Metrics panel pattern

The cleanest way to show batch metrics (SentiSense Score, Sentiment, Mentions, Social Dominance, anything similar) on a stock dashboard is a row-per-metric panel with a mini skyline bar and a lazy-loaded source breakdown.

Per-metric row:

┌─────────────────────────────────────────────────────┐
│ SentiSense Score                                    │
│ Sentiment × mentions          +0.65  ▮▯▮▮▯▮▯  ⌄    │
└─────────────────────────────────────────────────────┘

Components:

  • Title + 1-line description on the left, terse. The title is the metric name as the user sees it ("SentiSense Score", not sentisense_score).
  • Headline value on the right. Format depends on the metric: signed for [-1, 1] scores (+0.65), compact for counts (12.4K), percent for shares (3.42%). Color the value: green when positive (or above neutral), red when negative, neutral gray when null.
  • Mini skyline bar between value and chevron: 7 vertical bars of the last 7 readings, each green or red per direction, ~14-16px tall and ~56px wide. Bars anchor at the bottom and scale to the max-absolute value in the window. This signals trend at a glance without taking chart real estate.
  • Chevron on the rightmost edge, only for metrics that have a source breakdown. Tap to expand; fetch the breakdown lazily on first expand.

Expanded breakdown:

  • "Source mix" header in small caps.
  • One row per source with name, share percentage, and a thin progress bar (capped at 100%).
  • Sort by share descending so the dominant source reads first.

Why this works:

  • Multiple metrics fit comfortably in a narrow column on the dashboard.
  • The skyline encodes recent trend without claiming chart space (which the price chart already owns).
  • Lazy-loading the breakdown saves an API call per metric on the cold dashboard load. Most users never expand it; those who do pay the cost only for the metrics they care about.
  • Positioning the breakdown as a secondary "where this signal came from" panel matches what the data actually is (per the API shape gotchas section, distribution is share-of-voice, not per-source values).

Grounding the agent (tool ladder)

Stale knowledge is the #1 trust killer in a terminal. Never quote a number, date, headline, or rating from training data. Always ground the answer in a tool result. Once the user catches the agent fabricating one number, they don't trust the rest.

Equip the agent with a tool ladder, ordered by cost:

  1. Read what's already rendered (free). Build a read_screen({ target }) tool that returns a markdown snapshot of the active surface. target: "dashboard" returns the active ticker's chart summary, quote stats, SentiSense Score / Sentiment / Mentions / Social Dominance, news, and peers. target: "canvas" returns the currently-open artifact. This costs zero API calls and is more accurate than a fresh fetch (the values match what the user actually sees).
  2. Fetch live data for off-screen tickers. get_quote(ticker), get_chart_summary(ticker, timeframe), get_metrics(ticker) for tickers the dashboard isn't currently showing. One API call per tool.
  3. Pre-computed reports for thesis-flavored questions. A get_ai_summary(ticker, depth) tool that hits /api/v1/stocks/ai-summary is cheaper and more grounded than composing a thesis from scratch. Same for get_insights(ticker) (/api/v1/insights/stock/{ticker}).
  4. Topical search for non-ticker questions. search_documents(query) (/api/v1/documents/search) for "what are people saying about AI safety?" style asks.

read_screen snapshot shape. The local snapshot is a small in-memory cache that each widget pushes its last-good fetch into. The tool reads from the cache and formats markdown without re-fetching. Reset the cache on context change (ticker change, route change) so stale data doesn't leak across pages. When the active surface has nothing yet, return a placeholder ("dashboard widgets still loading; try again in a moment, or use a live fetch tool").

Host-side grounding configuration. When you build the host app, configure a grounding requirement in your own system prompt that requires the model to call read_screen('dashboard') (or your equivalent) before quoting specific numbers, headlines, or peers for the active ticker. Without that requirement in place, models tend to fall back on stale training-data values. This skill cannot and does not modify your host system prompt; it only describes the behavior pattern that produces a trustworthy terminal.

If a tool errors or returns empty, say so. "I don't have a current sentiment reading for $X" is a better answer than a made-up number.


Transparency UX (tool-call chips)

The terminal feels more trustworthy when the user can see what the agent is doing. Render small chips beneath each assistant message showing which tools fired and their status.

Format each chip as <icon> tool_name(arg-summary) where:

  • icon is while pending, on success, ! on failure.
  • tool_name is the tool's identifier (e.g. get_quote, read_screen).
  • arg-summary is a 1 to 3 word human label (e.g. $NVDA, dashboard, story 1a2b…).

Stream chips into the message as they fire, not at the end of the turn. The user sees real lookups happening rather than just a dots animation. Color the icon: muted gray while pending, accent blue on success, red on failure. Hover should show the full tool call (name + full args + status).

Chips build trust and give the user an implicit cost model. More chips means more API calls. Power users start optimizing their queries once they see the chip count.

Stream text deltas too. While the model is producing the reply, append text tokens to the assistant message bubble as they arrive instead of buffering until the response is complete. Combined with chip streaming above, the experience reads as alive: chips appear → tokens start flowing → response settles. Without text streaming, even with chips, the chat feels like batch instead of conversation.


Visual defaults (palette, type, motion)

The skill is renderer-agnostic, but a terminal that doesn't pick deliberate visual defaults will land on whatever its UI library suggests, which is usually generic Bootstrap blue and gray. Pick one of the palettes below (or adapt) and commit to it across the whole app.

Three palettes that work

Charcoal (calm, modern):

bg          #1C1C1E
surface     #2C2C2E
text        #F5F5F7
muted       #8E8E93
accent      #0A84FF
bull        #30D158
bear        #FF453A

Slate (technical, neutral):

bg          #0F172A   (slate-900)
surface     #1E293B   (slate-800)
text        #F1F5F9   (slate-100)
muted       #64748B   (slate-500)
accent      #38BDF8   (sky-400)
bull        #22C55E   (green-500)
bear        #EF4444   (red-500)

Cocoa (warm dark, premium):

bg          #1A1612
surface     #2A231D
text        #F0E9E0
muted       #7A6B5D
accent      #D4A843   (gold)
bull        #A8C97A
bear        #C97070

All three are dark-default. Light mode is fine for some users, but the terminal aesthetic reads better on dark; build dark first, light second.

Shared rules across palettes

  • Type. Sans-serif for prose (Inter, system-ui). Monospace for tickers and tabular data (SF Mono, JetBrains Mono, Fira Code). Don't mix three families; sans + mono is enough.
  • Numerals. Always tabular: font-variant-numeric: tabular-nums. Columns of numbers must align.
  • Radii. 12 to 16px for panels and cards, 6 to 8px for chips and small buttons. Avoid sharp corners (looks dated) and avoid pill-shaped buttons everywhere (looks consumer-y).
  • Motion. Short fades (~150-200ms) for chat messages, chips, and value changes. Slide-in (~250ms ease-out) for slide-over panels. Don't bounce or spring; the terminal feel rejects playfulness in motion.
  • Spacing. Tight columns of stats (8 to 12px row spacing). Breathing room only between top-level sections (24 to 32px). Generous side padding on the home omnibox (the home view is meant to feel calm).
  • Accent discipline. One accent color, used sparingly: tickers, focus states, the active tab, the headline value when above-neutral. Don't paint the whole UI with the accent. Bull and bear colors are reserved for direction-of-change values, not for general UI affordances.
  • Borders. Use thin 1px borders at low opacity (e.g. rgba(58, 58, 60, 0.4)) rather than full-strength dividers. A terminal feels heavy when every panel has a hard line around it.

What to avoid

  • Multi-color charts. Stick to one line color (or a green-vs-red split for bars). Rainbow palettes feel like a CSV viewer, not a terminal.
  • Purple. Reads as crypto / consumer in this product space. The three palettes above all dodge purple intentionally.
  • Gradients on data surfaces. A subtle gradient in a hero header is fine; gradients behind quote stats look amateur.
  • Heavy drop shadows. Use them only on slide-overs and modals. Cards inside the dashboard should sit flat.

Authoring Style (read carefully)

These rules make the terminal feel consistent across every turn. Apply them every time.

Tickers

  • Always render tickers as $NVDA, $TSLA, $AAPL: uppercase, dollar-prefixed. The host UI may style this in a brand color.
  • Never write "Nvidia" when the user typed "NVDA"; never expand tickers unless the user asked for company info.

Numbers and changes

  • Prices: two decimals. $190.20, not $190.2 and not $190.
  • Percent changes: signed, two decimals, percent sign. +1.23%, -0.45%. Always include the sign for changes.
  • Sentiment scores: integers 0-100, no unit.
  • Large dollar amounts: $1.2M, $2.5B, not raw digits.

Output structure

  • One signal per line. Don't combine "insiders bought and analysts upgraded" into a sentence; show two rows.
  • Numbers first, words second. $NVDA $890.12 (+1.4%) before any prose.
  • Vertical structure beats paragraphs. Tables and labeled rows beat sentences.
  • Round consistently across the screen.
  • No trailing prose unless the user asked for analysis. The screen IS the answer.

Tone

  • No preamble. Don't say "Here's what I found:" or "Based on the data:". Start with the answer.
  • No apologies. Don't say "I'm sorry, I can only...": silently degrade to what you can do.
  • No personalized recommendations. Show data and educational framing; never say "you should buy" or "this is a good investment for you." This skill is a data interface, not an advisor.
  • No emojis (the user's brand voice does not use them).

Hidden work

  • Parallelize independent calls. If a screen needs price + sentiment + insider + analyst, fire all four at once.
  • Cache /institutional/quarters for the session. Reuse latestQuarter across turns.
  • Never expose the call stack to the user. They want the answer, not the recipe.

Terminal Commands

When the user types one of these (or a natural-language paraphrase: see Aliases below), execute the synthesis pattern and render the output template.


open <TICKER>: Stock screen

The flagship command. One-screen composite report.

Calls (parallel):

  1. GET /api/v1/stocks/price?ticker={T}
  2. GET /api/v1/stocks/{T}/profile
  3. GET /api/v2/metrics/entity/{T}/metric/sentiment?period=30d
  4. GET /api/v1/insider/trades/{T}?lookbackDays=90
  5. GET /api/v1/analyst/{T}/consensus
  6. GET /api/v1/insights/stock/{T}/latest

Output template (monospace host):

╭─ {TICKER} · {Company Name} · {Sector} ────────────────────────────────╮
│  PRICE     ${price}  ({changePct}% today)                              │
│  TARGET    ${targetLow}-${targetHigh}  (mean ${targetMean}, {N} an.)   │
│  RATING    {consensusLabel}  ({upsidePct}% upside)                     │
│  MOOD      Sentiment {sentiment30d}  ({delta30d} 30d)                  │
│  INSIDERS  {insiderBuys} buys / {insiderSells} sells (90d)             │
│  AI        "{insightHeadline}"                                         │
╰────────────────────────────────────────────────────────────────────────╯

Output template (markdown host):

**$TICKER** · Company Name · Sector
| | |
|---|---|
| Price | $190.20 (+1.23%) |
| Target | $180-$250 (mean $210, 33 analysts) |
| Rating | Buy (10.5% upside) |
| Mood | Sentiment 67 (+8 30d) |
| Insiders | 3 buys / 0 sells (90d) |
| AI | "Margin guide raised, services beating consensus." |

Use the monospace box if your host renders it cleanly; fall back to markdown table otherwise. Same fields, same order, same density either way.


compare <A> <B>: Side-by-side compare

Calls: run the open workflow on each ticker in parallel.

Output template:

                       {A}              {B}
PRICE                  ${pA}            ${pB}
DAY %                  {dA}             {dB}
TARGET MEAN            ${tA}            ${tB}
UPSIDE %               {uA}             {uB}
CONSENSUS              {cA}             {cB}
SENTIMENT 30d          {sA} ({deltaA})  {sB} ({deltaB})
INSIDER NET 90d        {iA}             {iB}

Below the table, write a one-line "edge" summary: which ticker has the better composite and why. Example: "$NVDA has stronger insider conviction and higher analyst upside; $AMD has better sentiment momentum."


daily brief: Market open/close digest

Calls:

  1. GET /api/v1/stocks/market-status
  2. GET /api/v2/market-mood
  3. GET /api/v1/market-summary
  4. GET /api/v1/insights/market (top 5)
  5. GET /api/v1/stocks/prices?tickers=SPY,QQQ,IWM,DIA

Output template:

DAILY BRIEF · {date} · Market {OPEN/CLOSED}
────────────────────────────────────────────
INDEXES   $SPY {p} ({d}%)  $QQQ {p} ({d}%)  $IWM {p} ({d}%)  $DIA {p} ({d}%)
MOOD      {score} ({phase})  {weeklyChange} 7d
HEADLINE  {marketSummary.headline}

TOP SIGNALS
1. $T1  {insight1.headline}
2. $T2  {insight2.headline}
3. $T3  {insight3.headline}

screen smart-money: Convergence screener

Find tickers where insiders + congress + analysts all positive in the same 7-day window.

Calls:

  1. GET /api/v1/insider/cluster-buys?lookbackDays=7
  2. GET /api/v1/politicians/activity?lookbackDays=7 (filter transactionType=PURCHASE)
  3. GET /api/v1/analyst/market/activity?days=7&types=UPGRADE

Output template:

SMART-MONEY SCREEN · 7-day convergence
──────────────────────────────────────
TICKER   INSIDER    CONGRESS    ANALYST
$T1     {N} buys   {M} purch.  {K} upg.
$T2     ...

Sort by total signal count. Report top 10. If no convergence found, report top tickers in any single bucket as runners-up. Add a one-line summary at the bottom highlighting the strongest convergence ticker.


mood: Market sentiment snapshot

Calls: GET /api/v2/market-mood

Output template:

MARKET MOOD · {date}
─────────────────────
Composite      {score}  ({phase})    {weeklyChange} 7d
Social Sent    {v}  ({d})
Market Dir     {v}  ({d})
Fear Gauge     {v}  ({d})
Social Mom     {v}  ({d})
S&P 500 Trend  {v}  ({d})

SECTORS (top 3 / bottom 3)
Tech     {s} ({d})    Energy   {s} ({d})
Comms    {s} ({d})    Utils    {s} ({d})
Disc     {s} ({d})    Staples  {s} ({d})

flow <TICKER>: Smart-money flow on one ticker

Calls:

  1. GET /api/v1/insider/trades/{T}?lookbackDays=90
  2. GET /api/v1/politicians/activity?lookbackDays=90 (client-side filter to ticker)
  3. GET /api/v1/institutional/quarters then /api/v1/institutional/top-holdings/{T}?reportDate={Q}
  4. GET /api/v1/analyst/{T}/actions?days=90

Output template:

SMART-MONEY FLOW · $TICKER · 90d
─────────────────────────────────
INSIDERS    {N} buys (${$buys})  {M} sells (${$sells})  Net: {NET}
CONGRESS    {K} purchases  {L} sales
TOP 13F     1. {Inst1}  {shares1}  ({d1} qoq)
            2. {Inst2}  {shares2}  ({d2} qoq)
            3. {Inst3}  {shares3}  ({d3} qoq)
ANALYSTS    {U} upgrades  {D} downgrades  (recent: "{lastAction}")

news <TICKER>: Sentiment-tagged news + embeds

This is the command where the terminal feel really differentiates from a quote-and-dump tool. SentiSense returns documents (URLs + sentiment + source). The public document API provides derived analytics, not source content; it does NOT include the publisher's article headline. You produce the headline yourself using the Headline Resolution pattern below. Any retrieval from source URLs is your agent application's independent action, subject to the source platform's terms.

Calls:

  1. GET /api/v1/documents/ticker/{T}?limit=8 for the document feed
  2. GET /api/v2/metrics/entity/{T}/metric/sentiment?period=7d for context

Output template:

NEWS · $TICKER · 7d sentiment {score} ({delta})
─────────────────────────────────────────────────
1. [{sentiment}]  {resolvedHeadline}
   {source} · {time}
   {url}
2. ...

For Reddit/X URLs, render an embed card instead of a plain link (see Social Embeds below).


stories: Pre-clustered story feed

A curated alternative to raw news: SentiSense clusters related documents into named stories with our own AI-generated cluster titles and summaries. These titles are OUR content and are safe to display verbatim (no publisher copyright concern).

Calls:

  1. GET /api/v1/documents/stories?limit=10

Output template:

TODAY'S STORIES · {date}
────────────────────────
1. {cluster.title}                                           [{sentiment}]
   {cluster.summarizedContent}
   Tickers: $T1 $T2 $T3   ·   {documentCount} sources
2. ...

For per-ticker stories: GET /api/v1/documents/stories?ticker={T}&limit=5.


help or unrecognized input

Print the command list. Don't say "I don't understand": show what you can do.

COMMANDS
  open <TICKER>          stock screen
  compare <A> <B>        side-by-side
  daily brief            market digest
  screen smart-money     convergence screener
  flow <TICKER>          smart-money flow
  mood                   market sentiment
  news <TICKER>          recent news with embeds
  stories                pre-clustered story feed

OR ASK NATURALLY
  "is NVDA a buy here?"  "what's hot today?"
  "compare TSLA and RIVN" "WTF is going on with AAPL"

Natural Language Aliases

The user will rarely type the exact command syntax. Recognize the intent and run the closest workflow. Never ask "did you mean open NVDA?": just run it.

User saysRun
"show me $TICKER", "tell me about $TICKER", "what's $TICKER doing"open
"is $TICKER a buy", "should I look at $TICKER", "$TICKER thoughts"open + add a one-line educational framing summary at the bottom (data context, not a recommendation)
"what's hot today", "market today", "what's moving"daily brief
"smart money", "what are insiders buying", "what's the smart money doing"screen smart-money
"compare $A and $B", "$A vs $B"compare
"what's the market mood", "fear/greed", "is the market scared"mood
"WTF $TICKER", "why is $TICKER moving", "what's happening with $TICKER"flow + news (compose into one screen)
"news on $TICKER", "what's the news", "any headlines on $TICKER"news
"what's the story today", "what stories are happening"stories
"before earnings on $TICKER", "$TICKER earnings preview"flow + analyst estimates (use the pre-earnings synthesis below)

When the user asks something that doesn't map cleanly, default to open if a ticker is present, daily brief if not, and help only if neither.


Headline Resolution Pattern

The SentiSense public document API returns { url, source, sentiment, timestamp, summary? } for each document. By API design it does not include the publisher's article headline. The API provides derived analytics, not source content. If your application needs to display titles, resolve them yourself from the url field. Any content retrieval from source URLs is your agent application's independent action, subject to the source platform's terms.

Use this two-phase pattern, in order:

Phase 1: oEmbed for social URLs (fastest path)

If the URL is from a major social platform, fetch the platform's oEmbed endpoint. These are free, public, no-auth, and return rich pre-rendered content.

DomainoEmbed endpoint
reddit.com, *.reddit.comhttps://www.reddit.com/oembed?url={ENCODED_URL}
x.com, twitter.comhttps://publish.twitter.com/oembed?url={ENCODED_URL}&omit_script=true&dnt=true
youtube.com, youtu.behttps://www.youtube.com/oembed?url={ENCODED_URL}&format=json

The response includes title, author_name, and (for Reddit/X) an html field with a pre-styled embed. If the host UI supports inline HTML, use the html block. Otherwise extract the title.

Phase 2: Fetch + parse <title> for general URLs

For non-social URLs, if your tool surface includes a URL fetcher (e.g. WebFetch, Browse, fetch_url), call it on the URL and extract the <title> tag.

Optimization: only the first ~16KB of the page is needed (the <title> is in <head>, near the top). Don't read the full document.

Pseudo-pattern:

title = fetch(url, range=0-16384).extract("<title>")
if title and len(title) > 3:
  return title

Phase 3: URL slug fallback (zero-cost)

If you cannot fetch the URL (no fetch tool available, or fetch failed), derive a humanized title from the URL slug:

url:    https://www.reuters.com/technology/nvidia-q1-revenue-beats-2026-04-30/
slug:   nvidia-q1-revenue-beats
title:  "Nvidia Q1 Revenue Beats" (replace - with space, title-case, strip dates/hashes)
output: "Nvidia Q1 Revenue Beats: reuters.com"

Degrade gracefully so the news view never shows naked URLs.

Caching

If your environment supports session memory, cache resolved titles for 30 minutes keyed by URL. The same article often appears across multiple news queries.


Social Embeds

When rendering Reddit, X/Twitter, or YouTube URLs in the news view, the host UI may support rich embeds. Three rendering tiers, in order of preference:

  1. Native embed. If the host supports embedded iframes or widgets (e.g. a webview agent), inject the oEmbed html block directly. You get the platform's native look-and-feel.
  2. Card render. If the host supports markdown blockquotes or callout boxes, render as a quote card:
    > **Reddit · r/wallstreetbets** · 4h ago
    > "{post title or excerpt}"
    > {url}
    
  3. Plain link. Worst case, the standard news line format with the resolved title.

Never strip the url even when embedding: users want the option to click through.


Composition Templates

For complex queries the user may ask, you may need to compose multiple commands into one screen. Three reusable shapes:

Earnings preview

When the user asks "before earnings on $X" or "$X earnings preview":

EARNINGS PREVIEW · $TICKER · ER {date} ({daysOut}d out)
────────────────────────────────────────────────────────
SETUP       Sentiment {s30d} ({d30d} 30d) ·  Insiders {netInsider}
CONSENSUS   EPS ${epsConsensus} (revised from ${epsPrior})  ·  Rev ${revC}
ESTIMATES   Range ${epsLow}-${epsHigh}, {N} analysts
ACTIONS     {U} upgrades, {D} downgrades (30d)
            Recent: "{lastAction}"
THESIS      {one-line synthesis: bullish/bearish/mixed}

Calls: /profile, /analyst/{T}/estimates, /analyst/{T}/actions?days=30, sentiment 30d, insider 60d.

Sector deep-dive

When the user asks "what's happening in tech today" or "energy sector":

SECTOR · Technology · {date}
─────────────────────────────
MOOD       {sectorScore} ({phase})    {weeklyChange} 7d
TOP MOVERS $T1 +X%   $T2 +X%   $T3 +X%
LAGGARDS   $T4 -X%   $T5 -X%
DRIVERS    "{topInsight1}"
           "{topInsight2}"

Calls: /market-mood (filter to sector), /insights/market (filter by sector tag), /stocks/popular (filter by sector if available).

Watchlist brief

When the user provides multiple tickers (e.g. "watch NVDA AMD INTC"):

WATCHLIST · {date}
───────────────────
$NVDA   {price} {chgPct}   Mood {s}    {smartMoneyFlag}
$AMD    {price} {chgPct}   Mood {s}    {smartMoneyFlag}
$INTC   {price} {chgPct}   Mood {s}    {smartMoneyFlag}

The smartMoneyFlag is if any of (insider cluster buy, congress purchase, recent upgrade) hit in 7d; for the inverse; blank if neutral.


Synthesis Rules (recap)

The "terminal feel" comes from being predictable. Every screen follows these rules.

  1. Vertical structure. Tables and labeled rows beat paragraphs.
  2. Fixed widths. When showing comparisons, align columns so the user can scan top-to-bottom.
  3. Numbers first, words second. Lead each line with the value.
  4. One signal per line. Don't combine.
  5. Round consistently. Two decimals for prices, integer percent for changes, integer for sentiment scores.
  6. $TICKER always. Uppercase, dollar-prefixed.
  7. Sign every change. +1.23%, -0.45%, never bare numbers.
  8. No prose unless asked. The screen IS the answer.
  9. Parallelize calls. open fires 6 calls at once; don't await sequentially.
  10. Cache the quarter. /institutional/quarters rarely changes.

Anti-Patterns (Don't Do These)

These are the failure modes the skill is designed to steer around.

  • Don't narrate the work. "Let me look that up..." or "I'll need to call several endpoints..." is anti-pattern. The terminal does the work silently.
  • Don't apologize. "I'm sorry, I can only show preview data" is anti-pattern. Silently render what you have; flag (preview) in the corner if the user is on Free.
  • Don't issue personal recommendations. "$NVDA is a strong buy" or "you should sell $TSLA" is anti-pattern. This skill is a data interface, not an advisor. Show data and educational framing; let the user draw conclusions.
  • Don't ask follow-up clarifying questions for unambiguous asks. "Did you want price or sentiment for $NVDA?" is anti-pattern. Run the full open screen: it shows both.
  • Don't pretty-print one number. A user asking "$NVDA price" gets a one-line answer ($NVDA $890.12 (+1.4%)). They don't get a 30-line open screen for a price quote.
  • Don't hand-craft headlines. If the document API didn't return one, use the Headline Resolution pattern. Don't invent.
  • Don't hallucinate endpoints. No /api/v1/options/flow, /api/v1/dark-pool, /api/v1/earnings, /api/v1/alerts, /api/v1/chat, /api/v1/congress (use /politicians).
  • Don't show the API key in user-facing output. Ever.
  • Don't push PRO unless the user is hitting the wall. Free tier delivers great terminal experience. Mention PRO when a (preview) truncation is meaningfully limiting the answer.
  • Don't quote a price, percent move, headline, analyst rating, or earnings result from training data. That data is stale by definition. Fetch it every turn, even if the user just asked the same question minutes ago. The cost of one wrong number outweighs the cost of an extra get_quote.

API shape gotchas (worth memorizing)

Real shapes differ from what a naive reading of the endpoint URL might suggest. These cost time to discover the hard way; here they are upfront.

stocks/chart returns a bare ChartDataPoint[] at the top level, not { data: [...] }. Accept both shapes defensively:

const points = Array.isArray(raw) ? raw : (raw?.data ?? []);

Each point has both a timestamp (Unix ms) and a pre-formatted display date string. Read x-axis values from timestamp. Parsing the formatted date string falls back to the current year on some JS date parsers (e.g. Apr 6 becomes the current year instead of the year the bar belongs to).

entityMetrics/metrics returns ServingMetric[] where the scalar lives at metricValue.value.value (nested). Rank info, when present, is at metricValue.value.properties.{rank, percentile, totalStocks} or metricValue.properties.{rank, percentile, totalStocks}. Top-level value: number is a legacy fallback; handle it but don't rely on it.

function extractMetric(m) {
  return m?.metricValue?.value?.value
      ?? m?.metricValue?.value
      ?? m?.metricValue
      ?? m?.value
      ?? null;
}

entityMetrics/distribution wraps the payload in distribution (singular), not distributions:

{ entityId, metricType, timestamp, dimension: "source",
  distribution: { News: 37.4, Reddit: 37.4, X: 18.2, Substack: 7.0 } }

The values are share-of-voice percentages summing to roughly 100, not per-source sentiment scores. Render the breakdown as a "where this signal came from" panel, not as four sentiment bars.

stocks/getImages returns third-party CDN URLs that don't carry an embedded API key. Direct <img src> will 401/403. Wrap with the SentiSense anonymous image proxy:

https://app.sentisense.ai/api/v1/stocks/proxy-image?imageUrl=<encoded-url>

Story detail (documents/stories/:id) is a flat PublicStoryDetailDto, not the nested { cluster, entities, documents } you might expect. Inside aspectPerspectives[i], the fields bullishView and bearishView are structured objects (hook, risksOrCatalysts: string[], conclusion, confidence), not markdown strings. The top-level bullishView / bearishView ARE markdown strings. Same field names, different shapes. Type-check before calling string methods.

Chart timeframes accepted by stocks/chart: 1D / 5D / 1W / 1M / 3M / 6M / 1Y / ALL. Anything else falls back to 1M and logs a warning.

Sentiment, SentiSense Score, news clustering, AI summaries, and insights are batch metrics. Quote / price / chart points are real-time. Show the generatedAt timestamp on insight and AI summary surfaces so the user knows how fresh the analysis is. Don't claim "real time" on analytical surfaces.


Endpoint Quick Reference

For full schemas: https://sentisense.ai/skill.md.

PRICE         GET /api/v1/stocks/price?ticker={T}
              GET /api/v1/stocks/prices?tickers=A,B,C
              GET /api/v1/stocks/chart?ticker={T}&timeframe=1M|3M|6M|1Y
              GET /api/v1/stocks/{T}/profile
              GET /api/v1/stocks/market-status

SENTIMENT     GET /api/v2/metrics/entity/{T}/metric/sentiment?period=7d|30d|90d
              GET /api/v2/market-mood

DOCUMENTS     GET /api/v1/documents/ticker/{T}?limit=N    (no titles)
              GET /api/v1/documents/stories?limit=N       (cluster.title is OURS, safe)
              GET /api/v1/documents/stories?ticker={T}&limit=N

INSIDER       GET /api/v1/insider/cluster-buys?lookbackDays=N
              GET /api/v1/insider/trades/{T}?lookbackDays=N

CONGRESS      GET /api/v1/politicians/activity?lookbackDays=N
              GET /api/v1/politicians/member/{slug}/trades

INSTITUTIONAL GET /api/v1/institutional/quarters    (always FIRST)
              GET /api/v1/institutional/top-holdings/{T}?reportDate={Q}

ANALYST       GET /api/v1/analyst/{T}/consensus
              GET /api/v1/analyst/{T}/actions?days=N
              GET /api/v1/analyst/{T}/estimates
              GET /api/v1/analyst/market/activity?days=N&types=UPGRADE,DOWNGRADE

INSIGHTS      GET /api/v1/insights/stock/{T}/latest    (quota-counted)
              GET /api/v1/insights/stock/{T}/types
              GET /api/v1/insights/market

MARKET        GET /api/v1/market-summary

Wrap unwrap: most preview-gated endpoints return { isPreview, previewReason, data }. Iterate data.


Cost discipline

A cold ticker dashboard typically fans out 10 to 12 API calls on first load:

Surface elementCalls
Profile + logo1 to 2
Quote (header)1 (plus refresh interval)
Chart1
Metrics panel (4 metrics in parallel)4
Recent stories1
Peers (similar + batch prices)2
Cold total10 to 12

A grounded chat turn adds another 1 to 4 calls on top, depending on the tool ladder depth.

Mitigations:

  • Use Promise.allSettled for parallel widget fetches; widgets render independently as data arrives. Don't gate the page on the slowest call.
  • Module-level cache for popular-ticker lists and logos so they fetch once per session, not per visit.
  • Lazy-load source mix breakdowns when the user expands them. Don't fetch source distribution eagerly with the time-series.
  • Don't poll quotes faster than ~60s on the header. Tabular price grids don't need to poll at all.

Rate limits. Per-minute limits exist (Free 15 req/min, PRO 120 req/min). Exceeding them returns 429 with a Retry-After hint. Surface 429s gracefully ("rate limit reached, retrying in N seconds") rather than silently retrying or rendering a stale value.

Per-stock budget mental model. A heavy daily-driver visiting 15 tickers and chatting on each (~15 grounded turns × 3 calls) eats ~225 calls per session, comfortably under the PRO daily envelope. A light user (3 tickers per day, no chat) uses ~50. Plan defaults around that envelope so a typical user never sees a 429 in normal use.


Tier Summary

CommandFreePRO
openFull screen, AI insight quota-countedUnlimited
compareTwo tickers anytimeUnlimited
daily briefOnce per day comfortably (5 calls)Unlimited refreshes
screen smart-moneyTop items per bucketFull ranked lists
flowPreview sliceFull holder list, full history
moodFull data, no quota costSame
news8 most recentFull feed
stories10 most recentFull feed

PRO at $15/month: https://sentisense.ai/pricing