Skip to content

Commit fb4ff90

Browse files
ralyodioclaude
andauthored
feat(ticker): equity research in the pit, shipped as an installable plugin (#318)
`trade` could quote a price and place an order, but nothing answered the question that comes first. `moshcode ticker <SYMBOL>` renders advis0r.com's stored research report — score, technicals, SEC fundamentals, thesis, signals with their sources — plus verbs for signals, transcript search, company-name lookup, the stored report index, ranked discovery, and coverage stats. Same surface as `/ticker` at the mosh prompt. Every route used is public, read-only and unauthenticated, so there is no login verb and no write path. Two rules the renderers enforce rather than document: a stored snapshot always prints its `reportGeneratedAt`, whether the price is delayed and which feed produced it — a stale price dressed as a live one is the one failure mode that costs money — and the API's own disclaimer travels with the data. Also publishes moshcode's first Claude Code marketplace. `moshcode plugin install` adds it and installs `ticker@moshcode`, which provides /ticker, /signals, /research, /lookup, /reports and /discover inside the engine. The fan-out follows the same contract as skills (prd/0003 R8): every engine appears in the summary, and the ones with no plugin primitive say so. See prd/0008. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 9dd2a3f commit fb4ff90

21 files changed

Lines changed: 1769 additions & 4 deletions

.claude-plugin/marketplace.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
3+
"name": "moshcode",
4+
"description": "moshcode's own Claude Code plugins — the pit's slash commands, in your engine",
5+
"owner": {
6+
"name": "moshcoder",
7+
"url": "https://moshcode.sh"
8+
},
9+
"plugins": [
10+
{
11+
"name": "ticker",
12+
"description": "Equity research slash commands backed by advis0r.com: scored reports, extracted signals, transcript search, company-name lookup, and ranked watchlists.",
13+
"source": "./plugins/ticker",
14+
"category": "productivity",
15+
"author": {
16+
"name": "moshcoder",
17+
"url": "https://moshcode.sh"
18+
},
19+
"homepage": "https://github.com/moshcoder/moshcode#ticker",
20+
"keywords": ["stocks", "equity", "research", "markets", "advis0r"]
21+
}
22+
]
23+
}

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ or miss one that does. A test fails the build when it drifts.
4646
| `moshcode engines` | engines | list engines and installation status |
4747
| `moshcode tools` | tools | list workflow tools and installation status |
4848
| `moshcode trade` | tools | look up markets and trade through Alpaca |
49+
| `moshcode ticker` <br>`advisor` | tools | equity research from advis0r.com |
50+
| `moshcode plugin` <br>`plugins` | extend | install moshcode's slash commands into Claude Code |
4951
| `moshcode commands` | script | list built-in moshscript commands |
5052
| `moshcode completion` | extend | print a shell completion script |
5153
| `moshcode run` | script | run a moshscript |
@@ -211,6 +213,31 @@ Alpaca's CLI has no confirmation prompts; `--submit` intentionally removes
211213
MoshCode's preview guard. Live trading additionally requires Alpaca's `--live`
212214
opt-in or corresponding environment setting.
213215

216+
### Equity research (`moshcode ticker`)
217+
218+
Where `trade` is Alpaca's order book, `ticker` is the research desk:
219+
[advis0r.com](https://advis0r.com/api)'s public read-only API, rendered in the
220+
pit. No key, no login, no write routes, no binary to install:
221+
222+
```sh
223+
moshcode ticker NVDA # score, technicals, fundamentals, thesis, signals
224+
moshcode ticker lookup rivian # company name → RIVN
225+
moshcode ticker signals AAPL # what was said, quoted and sourced
226+
moshcode ticker search "data center" # across every indexed transcript
227+
moshcode ticker reports --limit 10 # the stored index, best score first
228+
moshcode ticker discover fusion # a ranked watchlist (slow — analyzes each candidate)
229+
moshcode ticker open NVDA # the shareable report page
230+
```
231+
232+
Add `--json` to any of them for the raw response. The same facade is `/ticker …`
233+
in the pit, and `MOSHCODE_ADVISOR_URL` points it at another instance.
234+
235+
Reports are **stored snapshots**, not live quotes: every response carries
236+
`reportGeneratedAt` and every renderer prints it, alongside whether the price is
237+
delayed and which feed produced it. Scores labelled `offline` come from
238+
deterministic rules rather than a model. It is a research aid, not advice, and
239+
nothing under `ticker` can place an order.
240+
214241
### Social posting from the pit
215242

216243
The pit can hand a prepared post to Bluesky or Nostr without storing either
@@ -305,6 +332,34 @@ from and five to rotate. Porkbun's API access is off by default and enabled
305332
per-domain — and its documentation tools work with no keys at all, which is a
306333
sensible way to try the server before trusting it with DNS writes.
307334

335+
## Claude Code plugins
336+
337+
MoshCode publishes its own plugin marketplace, so the pit's slash commands work
338+
inside your engine too:
339+
340+
```sh
341+
moshcode plugin list # what the marketplace ships, and who can take it
342+
moshcode plugin install # add the marketplace + install `ticker`
343+
moshcode plugin remove ticker # take it back off
344+
```
345+
346+
`ticker@moshcode` adds `/ticker`, `/signals`, `/research`, `/lookup`,
347+
`/reports`, and `/discover` — the same advis0r research surface described above,
348+
driven from inside a coding session. Restart the engine afterwards; a newly
349+
installed plugin is not live in a session that is already running.
350+
351+
The equivalent by hand:
352+
353+
```sh
354+
claude plugin marketplace add moshcoder/moshcode
355+
claude plugin install ticker@moshcode
356+
```
357+
358+
Claude Code is currently the only engine with a plugin primitive. The others are
359+
reported as skipped with a reason, the same way they are for skills, rather than
360+
being left out of the summary. `MOSHCODE_PLUGIN_SOURCE=.` installs from a local
361+
checkout instead of GitHub, which is how you try an unreleased plugin.
362+
308363
## Upgrade everything
309364

310365
```sh

bin/moshcode.mjs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ import { tradeArgs, tradeUsage } from "../src/trade.mjs";
1919
import { runUpgrade } from "../src/upgrade.mjs";
2020
import { selfUpdateCommand } from "../src/selfupdate.mjs";
2121
import { describeUninstall, uninstallPlan } from "../src/uninstall.mjs";
22-
import { mcpCommand, skillCommand } from "../src/integrations.mjs";
22+
import { mcpCommand, pluginCommand, skillCommand } from "../src/integrations.mjs";
23+
import { tickerCommand } from "../src/advisor.mjs";
24+
import { canOpenBrowser, openBrowser } from "../src/open-url.mjs";
2325
import { locate, tilde } from "../src/pwd.mjs";
2426
import { createPrd, listPrds, authoringPrompt } from "../src/prd.mjs";
2527
import { loginAuto, whoami, logout } from "../src/auth.mjs";
@@ -344,6 +346,18 @@ async function main() {
344346
propagateExit(r.code, r.signal);
345347
return;
346348
}
349+
if (cmd === "ticker" || cmd === "advisor") {
350+
const code = await tickerCommand(rest, {
351+
openUrl: (url) => canOpenBrowser() && openBrowser(url),
352+
});
353+
if (code) process.exitCode = code;
354+
return;
355+
}
356+
if (cmd === "plugin" || cmd === "plugins") {
357+
const code = await pluginCommand(rest);
358+
if (code) process.exitCode = code;
359+
return;
360+
}
347361
if (cmd === "console") {
348362
const code = await consoleCommand(rest);
349363
if (code) process.exitCode = code;

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
"src",
2626
"examples",
2727
"prd",
28+
".claude-plugin",
29+
"plugins",
2830
"install.sh",
2931
"README.md"
3032
],
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3+
"name": "ticker",
4+
"description": "Equity research slash commands backed by advis0r.com: scored reports, extracted signals, transcript search, company-name lookup, and ranked watchlists.",
5+
"version": "0.1.0",
6+
"author": {
7+
"name": "moshcoder",
8+
"url": "https://moshcode.sh"
9+
},
10+
"homepage": "https://github.com/moshcoder/moshcode#ticker",
11+
"license": "MIT",
12+
"keywords": ["stocks", "equity", "research", "markets", "advis0r"]
13+
}

plugins/ticker/README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# ticker — equity research in your engine 🤘
2+
3+
Slash commands backed by [advis0r.com](https://advis0r.com/api): scored research
4+
reports, extracted signals with sources, transcript search, company-name lookup,
5+
and ranked watchlists.
6+
7+
| command | what it does |
8+
| --- | --- |
9+
| `/ticker NVDA` | score, technicals, fundamentals, thesis, signals, sources |
10+
| `/signals AAPL` | what was actually said, quoted and sourced |
11+
| `/research data center` | full-text search across every indexed transcript |
12+
| `/lookup rivian` | company name → `RIVN` |
13+
| `/reports` | every stored report, best score first |
14+
| `/discover fusion` | a ranked watchlist for a topic (slow) |
15+
16+
## Install
17+
18+
```bash
19+
moshcode plugin install
20+
```
21+
22+
Or straight from Claude Code:
23+
24+
```bash
25+
claude plugin marketplace add moshcoder/moshcode
26+
claude plugin install ticker@moshcode
27+
```
28+
29+
Restart the engine afterwards — a newly installed plugin is not live in a
30+
session that is already running.
31+
32+
## How it works
33+
34+
Each command shells out to `moshcode ticker …`, which calls advis0r's public,
35+
read-only API. No key, no login, no write routes. With `moshcode` absent, every
36+
command falls back to `curl` against the same endpoints.
37+
38+
Point the commands at another instance with `MOSHCODE_ADVISOR_URL`.
39+
40+
## What this is not
41+
42+
A research aid, not advice. Reports are **stored snapshots** — every response
43+
carries `reportGeneratedAt`, and every command is instructed to print it, because
44+
a stale price presented as a live one is the one failure mode that actually costs
45+
someone money. Scores marked `offline` come from deterministic rules, not a model.
46+
47+
Trading lives behind a different verb: `moshcode trade` wraps Alpaca, previews
48+
orders by default, and requires an explicit `--submit`. Nothing in this plugin
49+
can place an order.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
description: Build a ranked watchlist for a topic (slow — it analyzes each candidate).
3+
argument-hint: "[topic]"
4+
allowed-tools: Bash(moshcode ticker:*), Bash(curl -sS https://advis0r.com/api/:*)
5+
---
6+
7+
## Task
8+
9+
Rank candidates for `$ARGUMENTS` (no topic → the default watchlist).
10+
11+
```bash
12+
moshcode ticker discover $ARGUMENTS --limit 10 --json
13+
```
14+
15+
Fallback: `curl -sS --max-time 180 "https://advis0r.com/api/discover?topic=<url-encoded>&provider=offline&horizon=2&limit=10"`
16+
17+
**This route runs an analysis per candidate and can take minutes.** Tell the
18+
user it is working before you start, and do not retry on a timeout — re-running
19+
it costs the same minutes again.
20+
21+
## Reading the response
22+
23+
`candidates` is ranked, each with `rank`, `ticker`, `companyName`, `lastPrice`,
24+
`overallScore`, `confidence`, `classification`, `thesis`, `primaryCatalyst`,
25+
`mainRisk`, `independentConfirmation`, plus liquidity fields
26+
(`bidAskSpreadPercent`, `avgVolume`, `float`, `marketCap`).
27+
28+
## Rules
29+
30+
- Lead with `rank`, `ticker`, `overallScore`, and `classification`.
31+
- **Print `mainRisk` next to every thesis.** A ranked list that shows only the
32+
bull case is a pitch, not research.
33+
- `provider: offline` means these scores are deterministic rules, not a model.
34+
Say which provider produced the ranking.
35+
- Flag illiquidity: a wide `bidAskSpreadPercent` or thin `avgVolume` matters
36+
more than the score for anything small-cap.
37+
- End with the response's own `disclaimer`.

plugins/ticker/commands/lookup.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
description: Find a ticker symbol by company name (rivian → RIVN).
3+
argument-hint: <company name>
4+
allowed-tools: Bash(moshcode ticker:*), Bash(curl -sS https://advis0r.com/api/:*)
5+
---
6+
7+
## Task
8+
9+
Resolve `$ARGUMENTS` to a ticker symbol.
10+
11+
```bash
12+
moshcode ticker lookup $ARGUMENTS --limit 10 --json
13+
```
14+
15+
Fallback: `curl -sS "https://advis0r.com/api/lookup?q=<url-encoded>&limit=10"`
16+
17+
## Reading the response
18+
19+
`matches` is a list of `{ symbol, name, exchange, hasReport }`.
20+
`hasReport: true` means advis0r already has a stored research snapshot.
21+
22+
## Rules
23+
24+
- Show every match with its exchange — "Delta" is an airline and a faucet company.
25+
- Mark which ones have a report, and offer `/ticker <SYMBOL>` for those.
26+
- One unambiguous match: say the symbol and go straight to offering the report.
27+
- No match: say the *directory* has no match, and do not invent a symbol.

plugins/ticker/commands/reports.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
description: Every stored advis0r research report, best score first.
3+
argument-hint: "[--limit n] [--sort recent|score|ticker]"
4+
allowed-tools: Bash(moshcode ticker:*), Bash(curl -sS https://advis0r.com/api/:*)
5+
---
6+
7+
## Task
8+
9+
List the stored reports.
10+
11+
```bash
12+
moshcode ticker reports $ARGUMENTS --json
13+
```
14+
15+
Fallback: `curl -sS "https://advis0r.com/api/reports?sort=score&limit=25"`
16+
17+
## Reading the response
18+
19+
`reports` is a list of `{ ticker, companyName, lastPrice, overallScore,
20+
confidence, classification, aiProvider, aiModel, sourceCount, signalCount,
21+
generatedAt }`, and `total` is how many exist.
22+
23+
## Rules
24+
25+
- Render as a table: ticker, score, classification, price, generated-at.
26+
- **`generatedAt` per row, always.** These are snapshots taken at different
27+
times; a table that hides that reads as one consistent as-of date.
28+
- A row with no `aiProvider` was scored deterministically, not by a model.
29+
- Offer `/ticker <SYMBOL>` for anything worth a closer look.
30+
- This is a coverage list, not a recommendation list. Rank order is score order.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
description: Full-text search across every indexed earnings transcript and article.
3+
argument-hint: <words to search for>
4+
allowed-tools: Bash(moshcode ticker:*), Bash(curl -sS https://advis0r.com/api/:*)
5+
---
6+
7+
## Task
8+
9+
Search the transcript index for `$ARGUMENTS`.
10+
11+
```bash
12+
moshcode ticker search $ARGUMENTS --limit 20 --json
13+
```
14+
15+
Fallback: `curl -sS "https://advis0r.com/api/search?q=<url-encoded>&limit=20"`
16+
17+
## Reading the response
18+
19+
`results` is a list of segments: `text`, `speaker`, `ticker`, `event_date`.
20+
The API tries full-text search first and falls back to a substring scan, so a
21+
hit is a hit — but relevance is not ranked. Read before summarizing.
22+
23+
## Rules
24+
25+
- Cluster the hits by ticker and say which companies came up, with dates.
26+
- Quote sparingly and attribute each quote to its speaker and ticker.
27+
- If nothing matches, say the *index* has no match — this searches advis0r's
28+
indexed corpus, not the whole web. Suggest `/lookup` if the query looks like
29+
a company name.

0 commit comments

Comments
 (0)