Skip to content

authorturker/sec-analyzer-ai

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

40 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“Š SEC Analyzer Bot

A Telegram bot that monitors SEC filings and insider trading for a configurable watchlist.

Single-file Python, two-language UI, no cloud required. Runs on Android (Termux) or any Linux box.

Python OpenRouter Telegram Tests License


✨ Features

πŸ“„ 13 Form Types 10-K, 10-Q, 8-K, Form 4, SC 13G/D, S-1, DEF 14A, and more
🌐 Two languages English + Turkish UI, switch live with /setlang en / /setlang tr
πŸ” Insider Trading Form 4 analysis with portfolio-wide sentiment score
πŸ“Š Sentiment trend /sentiment trend [days] β€” compare current vs. N-day-ago insider mood
πŸ†š Side-by-side compare /compare AAPL MSFT 10-K β€” single LLM call, four-axis comparison
πŸ—‚ Watchlist groups /addgroup tech AAPL MSFT NVDA β†’ /scangroup tech
πŸ“ˆ Price action Filing date β†’ +N day stock change appended automatically (free, no API key)
πŸ’Ή On-demand prices /checkprice AAPL [days] β€” last-N-day window summary (yfinance)
πŸ“° Yahoo Finance news /checknews AAPL [count] β€” latest headlines with publisher links (yfinance)
🧠 Smart Caching Never re-analyzes a filing already processed; old entries auto-pruned
πŸ€– Guided Setup First-run wizard, starts with language picker β€” no config file editing needed
βš™οΈ Full Telegram Control Manage tickers, forms, model, language, schedule, prompts from chat
⏰ Auto Scheduling Daily auto-scan at a time you set + hourly filing alarm (probe-only)
πŸ“Š Weekly Digest Sunday summary of everything analyzed that week
πŸ” Filing Diff Risk-factor comparison between current and previous 10-K / 10-Q
✏️ Custom Prompts Override the analysis prompt per form type
πŸ“„ Inline Original Button after each analysis to receive the raw filing as .txt
πŸ“‹ Markdown Reports /report sends this week's full analyses as a .md file
πŸ”” Webhook Mode Optional β€” faster response, lower battery use
πŸ“‘ Health Monitoring /status shows uptime, error counts, last scan/alarm times
πŸ§ͺ Tested 327 pytest tests for pure helpers, i18n, config cache, thread safety
⚑ OpenRouter Free LLM openrouter/free, $0 cost
πŸ“± Lightweight Single-file bot.py (~2900 lines), runs on a mid-range Android phone via Termux

πŸ—‚ Project Structure

sec-analyzer/
β”œβ”€β”€ bot.py                       # Main bot β€” everything in one file
β”œβ”€β”€ config.py                    # Thin loader β€” reads secrets from .env
β”œβ”€β”€ .env.example                 # Secrets template β€” copy to .env, fill in
β”œβ”€β”€ requirements.txt             # pip dependencies
β”œβ”€β”€ .gitignore
β”œβ”€β”€ lang/
β”‚   β”œβ”€β”€ en.json                  # English UI strings (default)
β”‚   └── tr.json                  # Turkish UI strings
β”œβ”€β”€ tests/                       # 327 pytest tests
β”‚   β”œβ”€β”€ conftest.py
β”‚   β”œβ”€β”€ test_alarm_buttons.py    # Interactive alarm + on-demand .md button
β”‚   β”œβ”€β”€ test_cfg.py              # Config cache + atomic mutate
β”‚   β”œβ”€β”€ test_checkprice_news.py  # /checkprice + /checknews formatters
β”‚   β”œβ”€β”€ test_collect_8k.py       # 8-K EX-99.* exhibit collection
β”‚   β”œβ”€β”€ test_compare.py          # /compare prompt builder
β”‚   β”œβ”€β”€ test_groups.py           # Watchlist groups
β”‚   β”œβ”€β”€ test_hotfixes.py         # Atomic JSON IO, wizard guard, digest week
β”‚   β”œβ”€β”€ test_i18n.py             # Language loader, t(), fallback
β”‚   β”œβ”€β”€ test_price.py            # Stooq parsing + price snippet
β”‚   β”œβ”€β”€ test_probe.py            # Alarm probe β€” whole-watchlist hit list
β”‚   β”œβ”€β”€ test_pure.py             # render, extract_section, build_prompt, …
β”‚   β”œβ”€β”€ test_sentiment.py        # Sentiment parse + trend rendering
β”‚   β”œβ”€β”€ test_startup_company.py  # Startup checks + Company cache
β”‚   β”œβ”€β”€ test_state.py            # Locks, raw store, cache TTL
β”‚   └── test_tg_llm.py           # tg() + llm() characterization tests
└── README.md

Runtime files (created automatically under ~/sec-analyzer/, not in the repo):

~/sec-analyzer/
β”œβ”€β”€ bot_config.json         # Live settings managed via Telegram
β”œβ”€β”€ cache.json              # Analyzed filings cache
β”œβ”€β”€ weekly_log.json         # Buffer for digest + /report
β”œβ”€β”€ sentiment_history.json  # /sentiment trend history
β”œβ”€β”€ price_cache.json        # Stooq price snippets cache
β”œβ”€β”€ previous_filings/       # For risk-factor diff
└── reports/
    └── bot.log

πŸš€ Quick Start

git clone https://github.com/authorturker/sec-analyzer-ai.git
cd sec-analyzer-ai
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env    # then edit .env with your 4 keys
python bot.py

On first run the bot launches a bilingual wizard β€” pick a language, choose default form types, add tickers, done.


βš™οΈ Configuration

Secrets live in a .env file (git-ignored). Copy the template and fill in four values:

cp .env.example .env
EDGAR_IDENTITY=Your Name yourname@email.com   # Required by SEC
OPENROUTER_API_KEY=sk-or-v1-...               # From openrouter.ai
TELEGRAM_BOT_TOKEN=123456:ABC...              # From @BotFather
TELEGRAM_CHAT_ID=123456789                    # From @userinfobot

config.py is a thin loader that reads these via python-dotenv β€” you never edit config.py itself. At startup the bot runs a health check and refuses to run if any of the four is missing, malformed, or still a placeholder.

Everything else β€” tickers, default forms, model, schedule, language, custom prompts, webhook URL, price-action lookforward, raw-filing cap (default 100), cache TTL β€” is managed live via Telegram commands and stored in ~/sec-analyzer/bot_config.json.


πŸ’¬ Commands

Scans

Command Action
Any news? Β· Check Β· /sec Scan watchlist with default forms
Insider Β· /insider Form 4 only across watchlist
Check all Β· /all SEC + Insider combined
/sentiment Portfolio-wide insider sentiment score
/sentiment trend [days] Compare current sentiment vs. N days ago (default 30)
/scanticker AAPL Scan single ticker, default forms, not added to watchlist
/scanticker AAPL 10-K 4 Scan single ticker with specific forms
/compare AAPL MSFT [FORM] Side-by-side comparison (default form: 10-K)
/checkprice AAPL [days] Last-N-day price summary β€” change, open/close, high/low (default 7)
/checknews AAPL [count] Recent Yahoo Finance headlines + publisher links (default 5, max 20)

Ticker Management

Command Action
/addticker AAPL Add ticker to watchlist
/addticker AAPL MSFT NVDA Bulk add
/removeticker AAPL Remove ticker
/listtickers Show full watchlist

Groups

Command Action
/addgroup tech AAPL MSFT NVDA Create or replace a named group
/removegroup tech Delete a group
/listgroups Show all groups and their members
/scangroup tech Scan a group with default forms
/scangroup tech 10-K 4 Scan a group with specific forms

Form Management

Command Action
/listforms All 13 supported forms + which are active
/addform SC 13G Add form to default scan
/removeform 8-K Remove form from default scan

Custom Prompts

Command Action
/setprompt 10-K <text> Override analysis prompt for a form type
/getprompt 10-K Show current custom prompt
/resetprompt 10-K Revert to default prompt
/listprompts Show all active custom prompts

Reports

Command Action
/report Send this week's full analyses as a .md file
(inline button after analysis) Receive the raw filing as a .txt

Scheduling & Alerts

Command Action
/setschedule 08:00 Auto-scan daily at 08:00
/setschedule off Disable auto-scan
/alarm Enable hourly filing alarm (probe only β€” no LLM, no cache write)
/alarm off Disable alarm
/digest Enable weekly Sunday digest
/digest now Send digest immediately
/digest off Disable weekly digest

Language & Webhook

Command Action
/setlang en / /setlang tr Switch UI + LLM-output language
/setwebhook <url> Switch to webhook mode (requires Flask + public URL)
/delwebhook Switch back to polling mode

Settings & Status

Command Action
/settings Show all current settings
/status Bot uptime, error counts, last scan time
/setmodel <model> Switch LLM model
/setlookback 60 Set lookback window in days (1–365)
/setchars 15000 Set max characters per section (1000–50000)
/setrawmax 500 Cap in-memory raw-filing cache (0 = unlimited)
/priceaction on / off Toggle the per-filing price-action snippet
/setlookforward 5 Days after filing for price change (1–90)

πŸ“‹ Supported Form Types

Form Description
10-K Annual report
10-Q Quarterly report
8-K Current events / material events
4 Insider buy/sell transactions
144 Restricted stock sale notice
SC 13G Passive major shareholder (>5%)
SC 13D Active / activist major shareholder
S-1 IPO registration statement
424B4 Prospectus
20-F Foreign company annual report
6-K Foreign company current report
DEF 14A Proxy / shareholder vote statement
11-K Employee retirement plan report

Each form has its own form-specific prompt β€” Form 4 triggers insider-sentiment analysis, S-1 triggers IPO attractiveness scoring, DEF 14A triggers proxy-vote analysis, and so on. Override per form with /setprompt.


πŸ“± Running on Android (Termux)

Install Termux from F-Droid β€” not the Play Store β€” then:

pkg update -y && pkg upgrade -y
pkg install -y python git tmux
git clone https://github.com/authorturker/sec-analyzer-ai.git
cd sec-analyzer-ai
pip install -r requirements.txt
cp .env.example .env && nano .env

Run in background with tmux:

tmux new -s sec
python bot.py
# Ctrl+B then D to detach

πŸ”” Webhook Mode (optional)

Webhook mode requires a publicly accessible HTTPS URL and Flask. On Android use Cloudflare Tunnel or ngrok:

pip install flask
pkg install cloudflared       # Termux
cloudflared tunnel --url localhost:5050

Register the tunnel URL from Telegram:

/setwebhook https://your-tunnel-url.trycloudflare.com

Restart the bot, and it switches to webhook delivery. Revert with /delwebhook and restart.


πŸ“Š Analysis Output Example

🏒 MU β€” 10-Q
πŸ“… 2026-04-03

πŸ“Š Quarter Performance
- Revenue: $8.7B (+18% YoY) β€” strong DRAM demand
- Gross margin: 34.2% β†’ 38.1% (HBM contribution)

πŸ”‘ Key Messages from Management
- AI server demand tracking above expectations
- HBM3E capacity expansion on track for Q3

⚠️ Notable Changes
- NAND pricing pressure continues
- China export restrictions remain an overhang

πŸ‘€ 3 Factors to Watch
1. HBM ramp cadence and yield rates
2. PC/mobile DRAM pricing recovery
3. Further export control developments

πŸ“Š Risk Factor Changes (vs previous 10-Q)
βž• Added: "Increased competition from Chinese DRAM manufacturers..."
βž– Removed: "Supply chain disruptions related to legacy node capacity..."

πŸ“ˆ Price action: +3.42% (2026-04-03 β†’ 2026-04-10)

────────────────────────────
[πŸ“„ View original filing]

πŸ§ͺ Tests

python -m pytest tests/ -q
231 passed in <1s

The suite covers the pure helpers (render_filing_message, extract_section, build_prompt, _parse_stooq_csv, _compute_price_change, parse_sentiment_signal, build_trend_lines, build_compare_prompt, _format_price_check, _news_extract, _format_news_list, _md_escape, …), the i18n loader (key parity, fallbacks, language switching, LLM-language hint), the config layer (snapshot isolation, atomic mutate, race protection), the thread-safe state stores, and the alarm probe (proves the hourly alarm makes no LLM calls and no cache writes). Network IO is not exercised β€” tests run offline.


πŸ’° Cost

$0. OpenRouter free tier β€” 50 requests/day per model. Loading $10 credit raises the limit to 1,000/day (only drawn on paid models). Stooq price data is also free.


πŸ”§ Troubleshooting

Error Fix
EDGAR identity invalid / startup check failed Edit .env and set the flagged value (e.g. EDGAR_IDENTITY=Real Name your@email.com)
403 Forbidden from SEC Same as above β€” SEC requires a real-looking identity
No time zone found with key UTC pip install tzdata (already in requirements.txt)
429 Too Many Requests Free-tier daily limit β€” switch model with /setmodel
⚠️ Analysis unavailable OpenRouter timeout/5xx β€” bot retries with exponential backoff
409 Conflict on getUpdates Two bot instances running β€” pkill -f bot.py then restart
400 Bad Request on sendMessage Markdown parse error β€” bot automatically retries as plain text
Bot goes silent after network error Reconnects automatically; check /status for error count
Webhook not receiving updates Verify public HTTPS URL; run /delwebhook to fall back to polling
Wizard shows English even though I want Turkish Step 0 of the wizard is bilingual β€” type /lang tr first
/checkprice or /checknews says "yfinance required" pip install yfinance (optional dep, ~50 MB)
Alarm fires but /check finds nothing Pre-v2.5 bug β€” update to current release (alarm is now probe-only)

πŸ“ Release Notes

v3.0

  • πŸ› Fix: 8-K analysis now delivers real content. extract_section() now recognises all standard 8-K items (1.01–9.01, including the 2023 cybersecurity item 1.05). A new _collect_8k_text() helper fetches the primary cover document and all EX-99.* attachments (press releases, earnings releases), concatenates them, and feeds the combined text to the LLM. Previously f.text() returned only the cover page boilerplate; the actual item body was never seen by the model.
  • Telegram hard-chunk. _split_message() now performs character-level hard-splitting for lines that exceed Telegram's 4096-char limit, preventing message loss on very long single-line outputs.
  • 8-K form-sensitive char limit. _FORM_MAX_CHARS["8-K"] = 20000 (up from the global 10 000) so press releases fit without truncation. Other form limits are unchanged.
  • llm() prompt clamp. Prompts > 30 000 chars are clamped with a log warning β€” a last-resort guard for multi-source prompts.
  • f.text() null guard. Empty or None filing text is now logged as a warning and the filing is skipped cleanly instead of propagating a TypeError.
  • llm() response guard. Missing or empty choices[0].message.content raises a ValueError immediately instead of causing a silent KeyError retry cascade.
  • Atomic JSON writes are now thread-safe. _atomic_write_json uses per-thread unique .tmp filenames ({name}.{pid}.{tid}.tmp) β€” prevents file corruption under concurrent writes.
  • weekly_log.json FIFO cap. Capped at 500 entries so the log cannot grow unbounded when the digest is disabled or failing.
  • fetch_new_filings fetch_text=False. The hourly alarm probe no longer downloads full filing text from EDGAR β€” it only checks existence, eliminating unnecessary rate-limit exposure.
  • load_* type guards. load_cache, load_price_cache, load_sentiment_history return a clean {} instead of crashing when JSON root is the wrong type.
  • All external network calls use retry(). fetch_yfinance_history, fetch_yfinance_news, fetch_stooq_daily, tg_send_document, tg_with_keyboard β€” all now wrapped with the central retry() + _backoff() infrastructure.
  • 327 pytest tests (up from 231 in v2.5) β€” characterization tests for tg() / llm() bespoke behaviour, corruption recovery, 8-K section extraction, EX-99.* collection.
  • /export β€” download this week's analysis log as a CSV file.
  • setMyCommands β€” the bot menu (/) is now populated via Telegram's command registry.
  • Ticker validation. /addticker and /addgroup reject entries that don't match ^[A-Z0-9.\-]{1,10}$.
  • Form input normalisation. SC13G, 10k, def14a and Unicode dash variants are all accepted and normalised.
  • Config lock (_cfg_lock). get_cfg returns a deep-copy; update_cfg / reset_cfg hold the lock across the read-modify-write cycle.

v2.5

  • πŸ› Fix: alarm probe-only. The hourly /alarm previously called the full scan pipeline in quiet mode β€” it silently consumed LLM quota, wrote to the cache, and announced an alert; then /check would return "no new filings" because the cache was already populated. The alarm now uses probe_new_filings_for_watchlist() which only asks EDGAR whether anything new exists and short-circuits on the first hit. Zero LLM calls, zero writes β€” the user runs /check manually after the alert.
  • Multi-language UI β€” single bot.py + lang/en.json + lang/tr.json, switch with /setlang. Replaces the old bot_en.py / bot_tr.py split.
  • /sentiment trend [days] β€” compare insider mood vs. N days ago, persistent history in sentiment_history.json.
  • /compare AAPL MSFT [FORM] β€” side-by-side LLM comparison of two tickers' latest filings.
  • /checkprice TICKER [days] β€” on-demand price summary via yfinance (optional dep). Default 7 days.
  • /checknews TICKER [count] β€” recent Yahoo Finance headlines + publisher direct links via yfinance.
  • Watchlist groups β€” /addgroup, /removegroup, /listgroups, /scangroup.
  • Price action snippet β€” every filing analysis gets πŸ“ˆ +3.4% (filing-date β†’ +5d) from Stooq (free, no API key). Toggle with /priceaction, tune with /setlookforward.
  • Wizard step 0 β€” language picker β€” first-run setup now starts bilingual.
  • In-memory config cache + atomic mutate_cfg β€” no more TOCTOU races on concurrent edits.
  • Thread-safe _raw_filings and _status stores with helper accessors.
  • scan_ticker refactored into fetch_new_filings / analyze_filing / render_filing_message / send_filing_result β€” testable, single-responsibility.
  • Startup validation of EDGAR_IDENTITY (placeholder/empty/format) β€” bot refuses to run with bad identity.
  • Cache TTL β€” entries older than cache_max_age_days (default 365) auto-pruned at startup.
  • Raw-filing cap β€” /setrawmax N enables FIFO eviction for long-running bots.
  • MarkdownV2-safe escape in digest snippets β€” characters like * and _ no longer get stripped.
  • 231 pytest tests β€” pure helpers, i18n, config cache, thread safety, sentiment, compare, groups, price, alarm probe.
  • Auto-discover languages β€” drop lang/<code>.json and it's picked up.
  • build_prompt simplified to a dict-dispatch with form aliases.

v2.4

  • Inline original-document button, Markdown report export, webhook mode, Markdown parse fallback.

v2.3

  • Connection health monitoring + /status, exponential backoff on all API calls.

v2.2

  • Weekly digest, custom analysis prompts, portfolio insider sentiment.

v2.1

  • Auto scheduling, hourly filing alarm, risk-factor diff.

v2.0

  • First-run wizard, 13 form types, form-specific prompts, Telegram settings management.

v1.x

  • Initial release, OpenRouter migration, insider trading scan, smart caching.

πŸ“š Support the Project

If you want to support this work, you can buy me a coffee.

Bitcoin : 178hyCd89p2QQnyUCL5y6hpzyJqu7QHz34

Lightning : turker@blink.sv

Solana : MXpoKvp1ZojjZ1fXYhgLCYfUo3R9U43jiCF8cEA1q1Y


⚠️ Disclaimer

This tool is for informational purposes only. Nothing it produces constitutes investment advice. Always do your own research before making investment decisions.


πŸ“„ License

MIT

About

A Telegram bot that monitors SEC filings (10-K, 10-Q, 8-K) and insider trading (Form 4) for a configurable list of tickers. Command-triggered via natural language. Powered by OpenRouter free LLMs. Runs on Android (Termux) or any Linux machine.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages