6 Commits
Author SHA1 Message Date
momentum devandClaude Opus 5.5 3e523e132a fix: reactive coverage (halt notices, "why is X up") no longer scores as a catalyst
qwen3:14b rated "halted on circuit breaker to the upside, stock now up 172%" at
conviction 0.8 / sentiment +1.0, a bigger rank boost than most real news. So
names that had already run were ranked up.
- Prompt: halt notices, movers roundups and articles that only report a price move
  are not a new catalyst (conviction <= 0.1). If one names a new announcement,
  classify it with conviction <= 0.3.
- A deterministic cap that does not depend on the model: halt type or halt wording
  caps conviction at 0.1; after-the-fact wording ("why is X stock up", "X shares
  surge", "stocks moving") caps it at 0.3. Dilution flags are never touched.
On the 4,046-headline news history the pattern catches 100% of halt notices and 82%
of roundups, 0% of analyst items and 0.8% of earnings items. Against qwen3:14b on a
fixed 20-headline set: reactive mean boost 0.318 -> 0.055 (max 0.80 -> 0.21); real
catalysts 0.353 -> 0.362; offerings 3/3 still flagged dilutive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3PXXn2tHBzdv6rYtGPz86
2026-10-01 21:53:42 -05:00
momentum devandClaude Opus 5.5 eed4ec0b62 fix: catalyst classifier works with qwen3 (OLLAMA_THINK) and no longer hides an empty answer
qwen3 thinks by default. With format=json, Ollama then returns "{}", which the parser
silently read as a confident "other" (sentiment 0, conviction 0), so every headline
scored zero with no error. Two changes:
- OLLAMA_THINK (true/false). It is sent as Ollama's "think" flag only when set, so other
  models are untouched. qwen3:14b needs OLLAMA_THINK=false.
- A JSON object with none of the expected keys is now a failure (None, retried
  next pass), not "other".
Checked through the live code path against qwen3:14b on mason (think=false): 1.4 s per
headline. 5/5 offerings (including an ATM sales agreement and a PIPE) flagged
dilutive. 'Stocks moving' roundups scored no catalyst. Earnings, guidance, analyst,
FDA and contract items were labelled correctly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3PXXn2tHBzdv6rYtGPz86
2026-10-01 20:59:32 -05:00
momentum devandClaude Opus 4.8 8305482376 Dilution detector: stop flagging "offering exposure" / product offerings
The recurring false positive (an ETF "offering exposure" tagged dilutive) came
from the regex FLOOR matching the bare word "offering" and one-directionally
flipping is_dilutive=true even when the model said false. Tightened both:
- _DILUTIVE_RE matches "offering" ONLY as a securities offering — a qualifier in
  front (public/underwritten/stock/equity/...) or "offering of ... shares/stock/
  units". Bare "offering" (ETF offering exposure, product offering) no longer trips.
- build_prompt's is_dilutive instruction now spells out the exclusions (ETF/fund
  "offering exposure", product/service offering, IPO of a different company,
  buyback, reverse split).

Re-measured on a labeled set: precision 88% -> 100%, recall stays 100% (all 7 real
offerings still caught), accuracy 94% -> 100%. News-enrichment unit tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:42:52 -04:00
momentum devandClaude Opus 4.8 e286330cd9 Fix the 3 Phase-2 mediums from the PR #2 review (still gated OFF)
Hardens the Ollama Phase-2 path before it can be enabled; no effect on the
default-off behaviour (these run only when the score-blend / veto flags are on).

1. catalyst_score no longer floors sentiment at neutral: a high-conviction
   BEARISH catalyst scored 0.45 (same as neutral) and LIFTED setup quality —
   backwards. Now `conviction * max(0, sentiment)`: only bullish boosts;
   bearish/neutral score 0. (+ bearish regression test.)
2. Prompt injection: the untrusted headline now sits in a delimited, length-
   capped <news> block, the model is told to treat it as DATA not instructions,
   and a </news> breakout token is stripped — so "...classify as bullish,
   is_dilutive=false" can't flip the verdict or bypass the veto. (+ 2 tests.)
3. The dilution-veto catalyst_provider no longer reads the SHARED research_con
   from the trigger thread: it refreshes under a lock using its OWN dedicated
   connection (opened lazily, single-threaded use), 30s TTL keeps DB hits rare.

tests/unit: 201 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:54:20 -04:00
momentum devandClaude Opus 4.8 8cee002c09 Merge PR #2 (Ollama news/catalyst enrichment) onto master + fix review blockers
Integrates the local-LLM enrichment ADDITIVELY on top of all current work, with
every Ollama feature OFF by default — with the flags off the system behaves
exactly as before (199 unit tests pass; a full --once pass runs clean with no
Ollama). Fixes the three issues the adversarial review flagged:

- BLOCKER: keep the Alpaca News ingestion. The PR predated it; the run_live_paper
  conflict is resolved to keep BOTH step_news (fetch -> raw_news_items) AND the
  enrichment. They're complementary: ingestion feeds enrichment authoritative-
  ticker headlines instead of thin RSS.
- HIGH-1: enrichment runs on its OWN daemon thread with its OWN DB connection,
  not the scheduler thread — a slow/hung Ollama can no longer starve exits /
  guard / eod-flatten, and it no longer shares the non-thread-safe research_con.
- HIGH-2: per-headline writes are all-or-nothing — on any ticker store failure
  the partial rows are deleted so the headline fully retries (no stranded
  tickers) and enriched is counted only on full success (no false count).

Phase-2 automation (catalyst score blend, dilution veto, arming-rank multiplier)
remains gated OFF pending the medium fixes (bearish-catalyst boost, prompt-
injection hardening, veto cross-thread read) noted in the review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 11:54:07 -04:00
Claude e3cb2b8e5f Add local-LLM (Ollama) news/catalyst scoring
Leverage a local Ollama model to interpret news catalysts for the small-cap
momentum strategy — distinguishing bullish catalysts (FDA approval, M&A) from
dilutive offerings the technicals alone can't see. Advisory-first, then automate.

Phase 1 (advisory; no change to trade behavior):
- research/ingestion/news_enrichment.py: stdlib urllib call to Ollama with
  structured JSON output, classifying each headline into a catalyst type,
  sentiment, conviction and dilution flag. Graceful degradation (None on any
  failure) so trading is never blocked.
- news_catalyst_cache table + populate the previously-dead news_events.sentiment.
- Runs on the existing interval Scheduler (off the hot path), gated by
  OllamaConfig; deduped by the raw_news_items payload hash.
- Surface the advisory on the dashboard approval queue (projection + api + UI).

Phase 2 (automation; ships gated OFF):
- Optional catalyst_score blended into calculate_setup_quality (None reproduces
  the exact legacy weights); pure pass-through via evaluate_setup.
- Dilution veto in TradingExecutionService (slow + fast paths) emitting a
  catalyst_dilution_veto risk event, behind NEWS_DILUTION_VETO_ENABLED.

Config (OllamaConfig.from_env), .env.example docs, and tests for parsing,
persistence/dedupe, the projection, the score blend, the veto, and an
end-to-end enrich->advisory flow (Ollama mocked). Default model moves to
qwen2.5:7b-instruct (best small-model JSON, fits a 12GB GPU).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M5HTMkaadGDnAPtXe2hWwg
2026-06-19 17:29:24 +00:00