Agentic Trading Lab
Talk to agents, and test trading ideas. Try AI trading agents on real market data with no code required.
AI translation
Messy SEC filings in, explainable 12-month distress alerts out.
Translation pending
FinancePython · TypeScriptGitHub @apatha32OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Covenant: credit early warning platform. Messy SEC filings in, explainable 12-month distress alerts out. Point-in-time XBRL pipeline, entity resolution, ML risk scores.
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Credit early-warning platform for lenders: messy SEC filings in, explainable 12-month distress alerts out.
A credit team at a private-credit fund or mid-market lender uploads its borrower list, often a messy spreadsheet of names and maybe tickers. Covenant:
covenant-credit-early-warning-platf.vercel.app: no sign-up, read-only. Visitors get an anonymous session that can read only the demo portfolio (50 real SEC filers, fictional lender).
Hosted on Vercel (Next.js) and Supabase; a GitHub Actions job refreshes SEC data for the demo companies weekly and re-scores them. See docs/deploy.md.
| Area | Result | Evidence |
|---|---|---|
| Ingestion | 4.0M XBRL facts, 337k filings and 5 FRED macro series for a 205-company dev sample; re-runs are idempotent (0 new rows on a second run); SEC rate limit held at ≤ 8 requests/sec | raw.ingest_runs counts |
| Bankruptcy event scan | 25/25 known Chapter 11 filers found in their petition year; 6 suspicious tags read and documented (mis-tags, co-registrants, subsidiary-only filings) | spot-check, review |
| Canonical financials | 25 metrics from 50+ XBRL tags with ordered fallback, year-to-date → quarterly derivation, restatement versions, scale-error quarantine; core metrics complete for 139/179 active companies | coverage report |
| Entity resolution | 100% auto-match precision at 82% coverage on a held-out 100-row messy file (typos, old names, missing/wrong tickers); 0 wrong matches on hand-written subsidiary/namesake traps | evaluation |
| Distress model | LightGBM PR-AUC 0.098 vs 0.019 for an Altman Z'' baseline (5x; base rate 1.1%) on 2022+ test data across ~6,000 companies; 65% of bankruptcies ranked in the riskiest 10%; flagged a median 14 months before filing (151 of 211); beats Altman in every backtest year 2015-2024 | model card |
| Engineering | Leakage test (features recomputed with all later data deleted must not change, and a planted leak must be caught), Postgres row-level security for multi-tenancy, 282 automated tests in CI against a real Supabase database, plus a web build | CI |
flowchart LR
subgraph Sources
SEC[SEC EDGAR<br/>XBRL facts, filings]
FRED[FRED<br/>macro series]
end
subgraph Batch["Python batch jobs"]
ING[ingest<br/>rate-limited clients]
PIPE[pipeline<br/>staging → curated]
ER[entity resolution]
ML[ml<br/>features, models]
end
subgraph Supabase
RAW[(raw Parquet<br/>Storage)]
PG[(Postgres<br/>raw · staging · curated · ml · app<br/>RLS per tenant)]
end
WEB[Analyst console<br/>Next.js]
SEC --> ING
FRED --> ING
ING --> RAW
RAW --> PIPE
PIPE --> PG
ER --> PG
ML --> PG
PG --> WEB
| Phase | Scope | Status |
|---|---|---|
| 0 | Monorepo, config, migrations, row-level security, CI | ✅ Done |
| 1 | SEC + FRED ingestion, dev sample, bankruptcy event scan | ✅ Done |
| 2 | Cleaning & canonical point-in-time financial model | ✅ Done |
| 3 | Entity resolution: upload, matching, review queue | ✅ Done |
| 4 | Features, labels, Altman / logistic / LightGBM models, SHAP, model card | ✅ Done |
| 5 | LLM risk signals from filing text (Claude Haiku), "Ask the filings" Q&A with citations (Claude Sonnet) | ✅ Done |
| 6a | Analyst console + read-only public demo, weekly refresh (live) | ✅ Done |
| 6b | API, borrower upload and review, alerts | Next |
| 7 | Hosting (Supabase + scheduled jobs + Vercel), drift monitoring, alerts | Planned |
Stack: Python 3.12, Polars, PostgreSQL / Supabase (RLS, Storage, pgvector), rapidfuzz, Pandera, scikit-learn, LightGBM with TreeSHAP, Claude API (Phase 5), Next.js (Phase 6). Full build spec: SPEC.md.
packages/common/ tripwire_common: settings (env), YAML config loaders, JSON logging, DB helpers
services/ingest/ SEC + FRED clients, rate limiter, raw Parquet landing, dev sample, Item 1.03 scan
services/pipeline/ raw -> staging -> curated (tag mapping, YTD derivation, versions, events);
entity/ = borrower upload, matching cascade, review queue
services/ml/ labels (text-checked), point-in-time features, models, evaluation, model card
services/ llm, api: one uv workspace member each (empty until their phase)
supabase/migrations/ schema + RLS, applied by the Supabase CLI
supabase/local/ supabase_shim.sql: Supabase roles + auth.uid() for plain-Postgres dev/tests
config/ models.yaml (LLM/ML settings) and domain/finance/ (the finance domain pack)
tests/ common/, db/ (RLS, schema guards), ingest/, pipeline/, entity/
reports/ committed data reports (bankruptcy scan, coverage, entity resolution)
apps/web/ Next.js console (Phase 6)
Requires uv and Docker.
cp .env.example .env # fill in keys as phases need them
make setup # uv sync --all-packages (installs Python 3.12 env)
make db-up # Postgres 17 + pgvector on localhost:54329
make check # ruff, mypy --strict, pytest
make db-reset # optional: wipe the local DB and apply shim + migrations to it
DB tests create and drop their own throwaway database on TEST_DATABASE_URL, so they never
touch your working data. Without a reachable server they skip; with REQUIRE_DB=1 (CI) they fail.
Needs SEC_USER_AGENT (name + email) and FRED_API_KEY in .env, and the local DB (make db-reset).
uv run python -m tripwire_ingest dev # tickers, sample, submissions, companyfacts, fred, scan
uv run python -m tripwire_ingest --help # individual commands
data/ (local storage backend) under content-addressed keys
(.../sha256=<payload hash>/...), indexed in raw.*_index / raw.source_files. Re-running
writes nothing new for unchanged data (counted as unchanged in raw.ingest_runs.counts).max_requests_per_second actually observed.config/domain/finance/dev_sample.yaml is generated once and committed, so
every re-run ingests the same 205 companies.reports/item_103_spotcheck.md (generated) plus
reports/item_103_review.md (manual review). Issues are logged to staging.quarantine.uv run python -m tripwire_pipeline all # staging, curated, events, coverage report
staging.facts; rule violations (non-USD unit, bad
dates, conflicting duplicates, ...) go to staging.quarantine. Output checked by a Pandera contract.curated.financial_metric, 25 canonical metrics from metric_mapping.yaml, with
the chosen tag, every version, and YTD-derived quarters (lineage in derivation).select * from curated.financial_metric_as_of('2024-02-20', '1') returns
each metric as known on that date. Features must read through it.curated.filing_event from 8-K items and NT 10-K / NT 10-Q.reports/metric_coverage.md.tripwire_pipeline.entity turns an uploaded borrower CSV into SEC companies:
name / company / borrower_name …, ticker / symbol …).
Extra columns are ignored.app.match_review with up to 5 candidates. Settings are in config/entity_resolution.yaml.app.entity_alias (RLS), reused on later uploads and
applied to every pending row with the same input. They are never shared across tenants.reports/entity_resolution_eval.md (scripts/eval_entity_resolution.py). On the
100-row hold-out file, 100% auto-match precision with 82% auto-matched. On hand-written
adversarial cases (subsidiaries, namesakes), 0 wrong auto-matches.uv run python -m tripwire_ingest bulk # SEC bulk zips (~3 GB) into raw storage
uv run python -m tripwire_pipeline training-data # 11k companies -> Parquet (same Phase 2 code)
uv run python -m tripwire_ml labels # classify 2.7k Item 1.03 8-Ks from their text
uv run python -m tripwire_ml dataset # quarterly point-in-time features + labels
uv run python -m tripwire_ml train # 3 models, backtest, registry, model card
config/domain/finance/labels.yaml. Estimated precision ≈ 88% from a fresh hand-checked
sample. Rows inside a bankruptcy are excluded.ml.model_version with a promotion rule.uv run python -m tripwire_llm sections # 10-K / 10-Q -> sections; Q&A chunks into Postgres
uv run python -m tripwire_llm eval # evals/text_signals.jsonl on Haiku and Sonnet
uv run python -m tripwire_llm estimate # exact token count on a sample -> cost
uv run python -m tripwire_llm signals --submit # Batch API (half price), refuses over budget
uv run python -m tripwire_llm signals --collect # verify every quote, load curated.text_signal
config/domain/finance/text_signals.yaml). Claude Haiku 5.5 returns
strict JSON; a signal is kept only if its evidence quote is found word for word in the section.
Results are cached by (accession, section, prompt version, model). Loaded: 1,650 signals across
all 50 demo companies from 2,861 section parts; 22 quotes were rejected for not matching the
filing. Total Claude spend for Phase 5 (eval, extraction, Q&A tests): about $3.ml.qa_limits. Citation check.Against a real Supabase stack instead of the shim: supabase db start (database only) or
supabase start (full local stack), then supabase db push to deploy to a linked project.
| Schema | Holds | Who can read |
|---|---|---|
raw | ingest run log, index of raw Parquet files in Storage | service_role |
staging | typed facts and filings, quarantine, LLM call cache | service_role |
curated | canonical point-in-time model: companies, metrics, events, text signals, chunks, macro | service_role |
ml | feature snapshots, labels, model registry, predictions, drift | service_role |
app | tenants, portfolios, matches, alerts, dispositions, audit log | authenticated, own tenant only |
Key decisions:
app table scopes rows to the caller's
memberships (app.user_tenant_ids(), app.has_role()). Children carry tenant_id and
reference parents by the composite (id, tenant_id), so a row cannot point at another
tenant's parent even though FK checks bypass RLS. Column-level UPDATE grants stop users from
moving rows across tenants or editing fields like an alert's reason.curated.financial_metric, ml.prediction, app.disposition,
app.audit_log and the raw indexes reject UPDATE/DELETE with a trigger. Restatements are new
versions; derived values (e.g. Q4 = FY − 9M) must carry derivation lineage.filed_date. ml.feature_snapshot
has a check that its max_source_filed_date <= as_of_date, a last-line leakage guard behind
the Phase 4 leakage test. Macro data is bitemporal (observation_date, release_date).util.cik (10-digit zero-padded) and
util.accession (0000320193-23-000106). Filings are keyed by (cik, accession) because one
accession can list several co-registrants (relevant to the 8-K 1.03 subsidiary issue).config/models.yaml (a test fails if a model
ID appears in Python); event codes, SIC exclusions and label rules live in
config/domain/finance/.models.yaml assumes Voyage
(voyage-3.5, 1024 dims, matching curated.filing_chunk.embedding). Confirm before Phase 5.BAMLH0A0HYM2; BAA10Y replaces it
(SPEC.md 3.9).