ta-catalyst
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ta-catalystWhy did NVDA fall 12% over the last month?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ta-catalyst
Explains why a stock moved over a past window — and says plainly when it cannot.
The pipeline runs three reads that are blind to one another, then reconciles them against a shared list of significant trading days:
read | what it sees | what it must not see |
Technical | price and volume, plus a distilled method playbook | any news, ever |
Company news | a local SQLite | the chart, the macro read |
Macro | QQQ, SPY, dollar index, 10y yield, gold, VIX — then macro news | the chart, company news |
Blindness is the point. Tell a model "NVDA missed earnings on March 5" and it will narrate the chart to fit that story, whether or not price action supports it. Two independent signals collapse into one, and you lose the ability to detect disagreement.
The product is the reconciliation
Each significant day gets one verdict:
classification | meaning |
| move with a matching catalyst of proportionate size |
| a real move, news was searched, nothing found |
| move much larger than the catalyst warrants |
| catalyst much larger than the move — priced in, or disbelieved |
| that stream never ran |
unexplained is the most informative outcome, not a failure. It implies flow,
positioning or genuine technical behaviour — and it is exactly where the
technical read deserves the most weight. None of these are derivable from
any single branch, which is why the branches stay blind.
Every result carries a coverage block. Absence of a search is not absence
of a catalyst, and the last two classifications exist so a gap in coverage is
never silently reported as a quiet market.
Related MCP server: deeplook
Quick start
Runs fully offline against deterministic fixtures — no API key, no network:
uv sync
uv run pytest -q
TA_MODE=stub uv run ta-catalyst-mcpMarket-data providers are optional extras, imported lazily — install only the one you use:
uv sync --extra futu # Futu OpenAPI (needs the OpenD gateway running)
uv sync --extra yfinance # Yahoo FinanceTo configure real data and models:
cp .env.example .env # fill in ANTHROPIC_API_KEY, PINECONE_API_KEY, ...
uv run ta-catalyst-check # reports what is configured and what is missingThe MCP tool
One tool, history_move_analysis. The caller is already an LLM that parsed the
user's request, so it passes structured arguments — an LLM choosing among
branches you will almost always run is pure failure surface.
// claude_desktop_config.json (or any MCP client)
{
"mcpServers": {
"ta-catalyst": {
"command": "uv",
"args": ["run", "--directory", "/path/to/ta-catalyst", "ta-catalyst-mcp"],
"env": { "TA_MODE": "real" }
}
}
}history_move_analysis(
ticker="NVDA",
months=3, # or explicit start_date / end_date
as_of="2026-09-20", # point-in-time cutoff for backtests
include_technical=True,
include_company_news=True,
include_macro=True,
)Returns narrative, verdicts, coverage, significant_days, catalysts,
technical_read, macro_context, diagnostics.
Two fields worth reading first: coverage, because false means not
searched; and technical_read.methods_read, because an empty list means
the model answered from generic technical-analysis priors instead of the
playbook — the failure mode that most looks like success.
How it fits together
START → scope → spine ─┬→ technical ──────────────────┐
├→ company news ───────────────┼→ reconcile ⇄ followup
└→ macro context → macro news ─┘ └→ narrative → ENDnode | model call? | does |
| thin | resolves ticker, window, branches (skipped when the caller passes them) |
| no | scans the window for significant days — the shared calendar every branch anchors on |
| yes | regime + method cards, blind to news |
| yes | queries the |
| no | six macro instruments, threshold crossings, ticker-vs-benchmark excess |
| yes | semantic search over Pinecone, driven by the macro observations |
| yes | the only node that sees all three streams |
| yes | one bounded second look at unexplained days |
| yes | the written answer |
Design rules the code actually enforces
Anomaly detection is code, not an agent decision. Which days mattered is
abs(return) > k·ATR plus gap and volume checks. Deterministic means
reproducible, cheap, and impossible to talk out of flagging something.
Measurements are tools; definitions are cards. The market-data layer emits
ma_order, dist_ma50_atr, ma50_slope_20d_atr — arithmetic with no free
parameters. What counts as an uptrend is handbook content, so it lives in a
versioned, citable markdown card, not in a Python function with magic numbers.
A test asserts no judgment labels leak into the extractor.
The deterministic node describes; the agent interprets. The macro node
emits "QQQ +2.86% (1.5 ATR)", never "tech rally". The agent composes the
search query. Code finds the location, the model reads the window.
Point-in-time everywhere. Every source hard-truncates at as_of —
prices, the news table, and the Pinecone filter. Lookahead leakage looks
exactly like success, so the guard is wired even on live paths.
Raw candles never reach model context. Selection reads ~30 scalars (~400 tokens). The playbook index is ~950 tokens resident; card bodies (600–1000 tokens each) load on demand.
Data stores
store | contract | notes |
Playbook |
| 14 method cards distilled from a technical-analysis handbook, with source page citations |
Company news | SQLite. Optional FTS in | |
Macro news | Pinecone metadata contract — no DDL exists, so the doc is the schema |
Create a news database from the shipped schema and fixture:
sqlite3 catalysts.db < resources/news_schema.sql
sqlite3 catalysts.db < resources/news_seed_fixture.sql # synthetic, fictional tickersThree things the news table does not store, and where they went:
impact_session is derived (a release after the close prices in the next
session; weekends roll to Monday), dedup is heuristic on title similarity,
and ingestion coverage is approximated from the ticker's row span. Each is
weaker than an explicit column would be, and each is documented in the schema.
Development
uv run pytest -q # full suite, offline
uv run ruff check src tests
uv run ruff format src testsTests cover the deterministic layers against hand-built fixtures, graph topology across every branch subset, both data stores, and the MCP surface via a real client round-trip. Tests touching live Futu data self-skip when the OpenD gateway is not running.
Status
Working and tested end to end on stubs and fixtures. Not yet exercised against live models or live data — the deterministic layers, graph topology, retrieval invariants and MCP contract are verified; agent output quality is not.
Known gaps, in rough priority order:
No golden set yet. Method selection should be evaluated separately from analysis quality — they fail for different reasons, and mixing them makes the eval uninformative.
The playbook covers chart patterns (one handbook chapter). Point-and-figure, candlesticks and short-term patterns are not distilled.
Session mapping rolls weekends but does not know market holidays, so an item published the evening before a holiday is attributed one session early.
The spine's event budget is tuned for a ~7-month window; on shorter windows clustered gaps can crowd out genuinely significant days.
The forward-looking half of the original design (technical outlook + upcoming catalysts) is not built.
License
MIT — code, schemas and configuration.
One carve-out worth understanding before you redistribute: the method cards in
playbook/ are distilled from a third-party technical-analysis handbook,
and carry source page citations for exactly that reason. The MIT grant covers
this repository's own work; it cannot relicense the underlying material, and
the base rates quoted throughout the cards originate in Bulkowski's research.
If you publish this repository, or swap in cards distilled from another book,
that provenance travels with them.
Available Tools
1 toolhistory_move_analysisHistorical move analysisARead-onlyIdempotent
Explain what moved a stock over a past window, and why.
Runs three INDEPENDENT reads that are blind to each other, then reconciles them:
A technical read of price and volume, with NO news context. Its value is that it is independent -- told the news first, a model narrates the chart to fit the story.
A search of company-specific catalysts (earnings, guidance, ratings, legal, product).
A deterministic read of the macro complex, then a search of macro news driven by what actually moved.
A deterministic scan first picks the significant trading days, so all three work from the same calendar. Each such day is then classified:
explained move with a matching catalyst unexplained a real move, news WAS searched, nothing found -- implies flow, positioning or genuine technical behaviour, and the technical read deserves MORE weight here overreaction move much larger than the catalyst warrants underreaction catalyst much larger than the move -- priced in no_ta / no_catalyst_coverage that stream was not run; NOT a finding about the market
Read coverage before the verdicts. A false there means the stream was
never run, and absence of a search is not absence of a catalyst.
Turning every branch off returns coverage only, with no verdicts.
| Name | Required | Description | Default |
|---|---|---|---|
| as_of | No | Point-in-time cutoff, ISO YYYY-MM-DD. Every data source hard-truncates here, so a backtest cannot see the future. Omit for live data. | |
| months | No | Lookback in months. Ignored when start_date and end_date are both given. | |
| ticker | Yes | Ticker symbol, e.g. 'NVDA'. Case-insensitive. | |
| end_date | No | Window end, ISO YYYY-MM-DD. | |
| session_id | No | Reuse a previous call's session to ask a follow-up without recomputing. Omit for an independent run. | |
| start_date | No | Window start, ISO YYYY-MM-DD. | |
| include_macro | No | Read the macro complex (QQQ, SPY, dollar index, 10-year yield, gold, VIX) and search macro news. | |
| include_technical | No | Run the blind technical read of the chart. | |
| include_company_news | No | Search company-specific catalysts. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/idempotent annotations, the description reveals substantial behavioral detail: the three reads are intentionally blind to each other, significant days are picked deterministically before any analysis, and each classification (explained, unexplained, overreaction, underreaction, no_ta/no_catalyst_coverage) carries a distinct meaning. This exceeds what annotations alone convey and contradicts nothing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: purpose is front-loaded, and the numbered reads plus bullet-like classification list make complex behavior scannable. It is long, but the length is mostly earned by the tool's inherent complexity; slight redundancy around the technical read's independence keeps it from being maximally tight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description correctly focuses on interpretation rather than return shape. It explains the significance of coverage false values, warns that absence of a search is not absence of a catalyst, and clarifies how deterministic day selection binds the three streams together. Nothing needed to invoke or interpret the tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents every parameter well and the description does not need to repeat them. The description does add contextual meaning for branch flags, such as the technical read running with no news context and all branches off returning coverage only, but it does not add per-parameter syntax or format details beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Explain what moved a stock over a past window, and why.' It then details the exact method (three independent reads reconciled into verdict categories), so an agent knows precisely what the tool does and how it differs from a generic historical quote or news tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There are no sibling tools to distinguish against, but the description gives clear operational guidance: which branches can be toggled, what happens when branches are disabled, and how to interpret coverage before verdicts. It does not explicitly state 'use when X instead of Y' because no alternatives exist, so it stops just short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.1.0- First observed
history_move_analysis
TDQS
Scored across 1 tool
With only one tool, there is no possibility of confusion or overlap. The tool's purpose is clearly defined, so an agent cannot misselect among alternatives.
The single tool name follows a clean verb_noun pattern (history_move_analysis) and is descriptive of its functionality. Consistency is trivially maintained with one tool.
A single tool feels thin for a server, even a specialized one. It is borderline acceptable given the complexity of the tool, but the calibration suggests 1-2 tools is on the low side.
The tool thoroughly covers the stated domain of explaining stock moves by integrating technical, catalyst, and macro analysis, with options to toggle branches. It provides a complete workflow for its narrow purpose.
Maintenance
Related MCP Connectors
Explains why stocks moved. 'Why did Tesla drop?' 'What happened to NVDA?' S&P 500/NASDAQ/Dow.
Analyze stocks and SEC filings to surface key insights, from price and volume to insider activity…
Verify stock theses against public filings with a source-backed evidence ledger.
Analytical engine for US-listed equities: screens, rankings, and event studies in plain English.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceExplains why US stocks moved. AI analysis for 'why did Tesla drop?' queries. Covers S\&P 500, NASDAQ 100, Dow 30 (~550 stocks).1-
- AlicenseAqualityBmaintenanceResearches any company in ~10 seconds using 10 data sources. Returns structured reports with bull/bear verdict for stocks, crypto, and private companies.212AGPL 3.0
- FlicenseNot gradedqualityBmaintenanceEnables aggregated investment research by combining data from Yahoo/SEC EDGAR, Finnhub, and GDELT with analysis modules for price momentum, news signals, and estimate revisions.-
- AlicenseNot gradedqualityDmaintenanceEnables financial research on US-listed equities by answering natural language questions with structured data from fundamentals, prices, earnings, and insider activity.3MIT