Skip to main content
Glama
justin-hsun

ta-catalyst

by justin-hsun

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 news table

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

explained

move with a matching catalyst of proportionate size

unexplained

a real move, news was searched, nothing found

overreaction

move much larger than the catalyst warrants

underreaction

catalyst much larger than the move — priced in, or disbelieved

no_ta / no_catalyst_coverage

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-mcp

Market-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 Finance

To 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 missing

The 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 → END

node

model call?

does

scope

thin

resolves ticker, window, branches (skipped when the caller passes them)

spine

no

scans the window for significant days — the shared calendar every branch anchors on

technical

yes

regime + method cards, blind to news

ticker_cat

yes

queries the news table, headlines first, bodies on demand

macro_context

no

six macro instruments, threshold crossings, ticker-vs-benchmark excess

macro_cat

yes

semantic search over Pinecone, driven by the macro observations

reconcile

yes

the only node that sees all three streams

ta_followup

yes

one bounded second look at unexplained days

narrative

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

playbook/<name>/SKILL.md

14 method cards distilled from a technical-analysis handbook, with source page citations

Company news

resources/news_schema.sql

SQLite. Optional FTS in news_optional_fts.sql

Macro news

resources/macro_vectorstore_schema.md

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 tickers

Three 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 tests

Tests 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 tool
history_move_analysisHistorical move analysisA
Read-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:

  1. 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.

  2. A search of company-specific catalysts (earnings, guidance, ratings, legal, product).

  3. 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.

ParametersJSON Schema
NameRequiredDescriptionDefault
as_ofNoPoint-in-time cutoff, ISO YYYY-MM-DD. Every data source hard-truncates here, so a backtest cannot see the future. Omit for live data.
monthsNoLookback in months. Ignored when start_date and end_date are both given.
tickerYesTicker symbol, e.g. 'NVDA'. Case-insensitive.
end_dateNoWindow end, ISO YYYY-MM-DD.
session_idNoReuse a previous call's session to ask a follow-up without recomputing. Omit for an independent run.
start_dateNoWindow start, ISO YYYY-MM-DD.
include_macroNoRead the macro complex (QQQ, SPY, dollar index, 10-year yield, gold, VIX) and search macro news.
include_technicalNoRun the blind technical read of the chart.
include_company_newsNoSearch company-specific catalysts.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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. 1 tool updatev0.1.0
    • First observedhistory_move_analysis

TDQS

A4.4/5.0

Scored across 1 tool

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count3/5

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.

Completeness5/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Explains why US stocks moved. AI analysis for 'why did Tesla drop?' queries. Covers S\&P 500, NASDAQ 100, Dow 30 (~550 stocks).
    1
    -
  • A
    license
    A
    quality
    B
    maintenance
    Researches any company in ~10 seconds using 10 data sources. Returns structured reports with bull/bear verdict for stocks, crypto, and private companies.
    2
    12
    AGPL 3.0
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables aggregated investment research by combining data from Yahoo/SEC EDGAR, Finnhub, and GDELT with analysis modules for price momentum, news signals, and estimate revisions.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables financial research on US-listed equities by answering natural language questions with structured data from fundamentals, prices, earnings, and insider activity.
    3
    MIT