Bob the Bot
Portfolio Announcements Intelligence Agent
Bob the Bot is an automated AI agent that monitors ASX announcements for companies in my investment Portfolio, analyses important updates, and delivers a daily intelligence briefing.
The goal is simple:
Read everything so I don't have to. Surface the signal and ignore the noise.
Instead of manually checking ASX announcements every day, Bob retrieves new filings, downloads important documents, runs AI analysis when appropriate, and sends a concise summary.
System Overview ┌──────────────────────────┐ │ My Portfolio │ │ tickers.yaml │ └─────────────┬────────────┘ │ ▼ ┌──────────────────────────┐ │ ASX Announcements │ │ Data Retrieval │ └─────────────┬────────────┘ │ ▼ ┌──────────────────────────┐ │ Bob the Bot │ │ agent.py Engine │ └─────────────┬────────────┘ │ ┌────────────────┼────────────────┐ ▼ ▼ ▼
┌───────────────┐ ┌────────────────┐ ┌────────────────┐ │ Announcement │ │ Document │ │ AI Analysis │ │ Classification │ │ Retrieval │ │ (OpenAI API) │ └───────┬───────┘ └───────┬────────┘ └────────┬───────┘ │ │ │ ▼ ▼ ▼
┌───────────────┐ ┌────────────────┐ ┌────────────────┐ │ Portfolio │ │ Google Drive │ │ Strawman Post │ │ Intelligence │ │ Document Store │ │ Draft Generator│ └───────┬───────┘ └────────┬───────┘ └────────┬───────┘ │ │ │ └──────────────┬────┴─────────────┬─────┘ ▼ ▼
┌───────────────┐
│ Daily Email │
│ Intelligence │
│ Digest │
└───────────────┘
What Bob Does
Every day Bob automatically:
Monitors ASX announcements for companies in my Portfolio
Identifies announcements released within the last 24 hours
Avoids reporting the same announcement on consecutive days by tracking seen announcement IDs between runs
Filters out routine filings and low-impact updates
Downloads PDFs for meaningful announcements
Extracts text from reports and presentations
Runs AI analysis on major announcements
Generates short summaries for quick review
Saves important documents to Google Drive
Sends a daily intelligence briefing email
Bob focuses on signal over noise.
Example Output
Bob sends a daily briefing structured like this:
Bob the Bot Daily Announcements Digest — last 24 hours
HIGH IMPACT
DRO — FY Results
Revenue growth strong but margin compression continues.
Analysis: [Detailed AI analysis]
Open: https://www.asx.com.au/...
Drive: https://drive.google.com/...
MATERIAL
TWE: Retirement of Chief Financial Officer So what: Leadership transition likely operational rather than strategic.
Open: https://www.asx.com.au/...
FYI
ABB: Appendix 3Y Director Interest Notice So what: Routine filing. Portfolio Monitoring
Companies are defined in:
tickers.yaml
Example:
asx:
- DRO
- AR9
- CAT
- ABB
- TWE
Bob retrieves announcements for each ticker.
Announcement Source
Bob retrieves announcements directly from the ASX announcements feed.
Example endpoint:
https://www.asx.com.au/asx/v2/statistics/announcements.do?asxCode=DRO
Information extracted includes:
announcement title
release date
announcement URL
PDF document link
Only announcements from the last 24 hours are processed.
Announcement Classification
Announcements are automatically categorised:
Category Description RESULTS_HY_FY Half year or full year results ACQUISITION M&A transactions CAPITAL_OR_DEBT_RAISE Equity issuance or refinancing CONTRACT_MATERIAL Major contracts or strategic updates OTHER Routine filings
Classification uses keyword detection and content signals.
Document Retrieval
For important announcements Bob retrieves the PDF.
Retrieval pipeline:
Step 1 — Direct Download
Using Python requests.
Step 2 — Browser Simulation
If blocked by the ASX consent page:
Playwright
simulates a browser to retrieve the file.
Step 3 — HTML Fallback
If the PDF cannot be retrieved, Bob extracts text from the webpage.
Text Extraction
When a PDF is downloaded:
pypdf
extracts the text.
Bob also checks that the content is meaningful to avoid analysing:
empty documents
ASX legal disclaimer pages
broken downloads
AI Analysis
Bob uses the OpenAI API.
Default model:
gpt-4o-mini
AI analysis is used selectively to control cost.
Deep Analysis Triggers
Detailed AI analysis runs only for major events:
Half-year results
Full-year results
Acquisitions
Capital raises
Debt refinancings
These produce:
• full AI analysis • a Strawman-ready investor post • saved documents in Google Drive
Quick Summaries
Most announcements receive a short two-line summary:
DRO: $21.7m Western Military Contracts So what: Adds to defence pipeline but unlikely to materially move revenue this year. Google Drive Storage
Important documents are automatically uploaded to Google Drive.
Stored documents include:
results reports
investor presentations
acquisition documents
capital raise materials
Drive links are included in the email.
Email Alerts
The daily digest contains:
HIGH IMPACT
Major announcements requiring deep analysis.
MATERIAL
Price-sensitive announcements requiring attention.
FYI
Routine announcements included for completeness.
SILENCE
If no announcements occurred:
No announcements in the last 24 hours.
When this happens, Bob now also includes:
- A Joke of the Day
- A Political Cartoon of the Day link Special Routing
Some tickers trigger additional alerts.
Example:
AR9
If AR9 appears in the announcements:
Bob sends a separate email digest to my brother.
Architecture
Core system files:
agent.py
Main orchestration engine.
playwright_fetch.py
Browser automation for retrieving ASX PDFs.
prompts.py
AI prompts used for announcement analysis.
tickers.yaml
Portfolio company list.
Workflow
Bob runs automatically using GitHub Actions.
Example schedule:
5 1 * * *
Which corresponds to:
09:05 Singapore Time
Bob runs entirely in the GitHub cloud environment.
Your computer does not need to be on.
Required Environment Variables
Configured using GitHub Secrets:
EMAIL_FROM EMAIL_TO EMAIL_APP_PASSWORD OPENAI_API_KEY GDRIVE_SERVICE_ACCOUNT_JSON GDRIVE_FOLDER_ID BROTHER_EMAIL Forcing a Re-run for a Specific Ticker
Bob tracks every announcement it has already processed in state_seen.json.
Seen entries expire after 72 hours, but within that window any announcement that was processed in a previous run is skipped — even if you trigger Bob manually on the same day.
To re-process announcements for one or more tickers without clearing the entire state file:
- Go to the Actions tab in GitHub.
- Select the Daily Announcement Digest workflow.
- Click Run workflow.
- Enter the ticker(s) in the "Comma-separated ASX tickers to force re-run" field (e.g.
NHCorNHC,BHP). - Click Run workflow.
Bob will bypass the dedup check only for those tickers and re-process any of their announcements that fall within the current HOURS_BACK window — all other tickers remain deduplicated as normal.
This can also be triggered by setting the FORCE_RERUN_TICKERS environment variable when running locally:
FORCE_RERUN_TICKERS=NHC python agent.pySafety Limits
To prevent runaway API usage:
MAX_PDFS_PER_RUN = 10 MAX_LLM_CALLS_PER_RUN = 15 MAX_ANNOUNCEMENTS_PER_TICKER = 12 Technology Stack
Bob is written in Python.
Key libraries:
requests beautifulsoup4 pypdf playwright openai google-api-python-client pyyaml Deployment
Bob runs fully in the cloud via:
GitHub Actions
Execution environment:
Python 3.11 GitHub Hosted Runner Future Improvements
Potential upgrades:
LSE announcement monitoring
broker research ingestion
earnings model extraction
insider trading detection
Slack or Telegram alerts
portfolio dashboard visualisation
Why Bob Exists
The ASX produces hundreds of announcements every week.
Most are irrelevant.
Bob's job is to read everything so I don't have to, highlight the important information, and deliver a clean intelligence briefing for my Portfolio.
Wally is a second, separate agent that screens multiple watchlists for stocks trading near their 52-week lows.
- Loads watchlists from
watchlists/*.yaml - Fetches latest price + 52-week low/high per ticker
- Flags stocks within 5% of 52-week low:
distance_to_low_pct = ((current - low_52w) / low_52w) * 100- flagged (
near_low) whendistance_to_low_pct <= 5
- Also flags stocks at or below a configured per-ticker target ("buy") price:
below_targetwhencurrent <= target_pricedistance_to_target_pct = ((current - target_price) / target_price) * 100- a ticker is flagged when
near_low OR below_target, and below-target tickers get the same treatment as near-low ones (range + value charts, email detail, dashboard row)
- Creates:
- 10-year price-vs-value chart when valuation config exists
- Sends an HTML email report (with chart attachments)
- Writes machine-readable JSON output under
outputs/YYYY-MM-DD/
Supported format — a plain string list still works exactly as before:
name: JM Watch List
tickers:
- ARB.AX
- QOR.AXTarget ("buy") prices are optional and fully backwards-compatible. Any of the three forms below may be mixed in one file; plain strings simply carry no target:
name: JM Watch List
tickers:
- ARB.AX # plain string — no target
- ticker: CGS.AX
target_price: 1.20 # per-entry mapping
- ticker: QOR.AX
buy_price: 3.50 # `buy_price` is an accepted alias (used by the TII list)
# ...or a separate top-level block keyed by ticker:
targets:
ARB.AX: 30.00
CGS.AX: 1.20Loader rules: ticker keys are normalised with .strip().upper(); prices are
coerced to float and anything <= 0 or non-numeric is ignored, so a bad
value never produces a false flag. GBX pence prices may be written the way the
source spreadsheet holds them ("500.00p", "9,500.00p") — the loader strips
the thousands comma and trailing p to a numeric pence value (compare against a
pence-quoted quote). When
CGS.AX has target_price: 1.20, it is flagged the moment it trades at or
below 1.20 and appears in the email with Target 1.20 and Trigger
Below target (or Near 52w low + Below target when both apply).
Files used:
watchlists/tii_watchlist.yamlwatchlists/jm_watchlist.yamlwatchlists/aussie_tech_watchlist.yamlwatchlists/tii75_watchlist.yaml
Place per-ticker config at:
valuations/<ticker_lower_with_underscores>.yaml- e.g.
valuations/bhp_ax.yamlforBHP.AX
- e.g.
Wally uses configured EPS/dividend series to plot value lines. If missing, the report states: No valuation config found yet for this ticker.
Workflow: .github/workflows/wally_watchlists.yml
- Tue + Fri: standard watchlists (
tii,jm,aussie_tech) - Fri only:
tii75is attempted, then Python applies fortnightly gate- gate logic in
wally.config.should_run_tii75 - adjustable via env
TII75_ANCHOR_ISO_WEEK
- gate logic in
As of the latest update, Wally supports combined email mode to consolidate all watchlist results into a single email:
# Run all watchlists (standard + TII75) with combined email
python -m wally.main --all-combined
# Run standard watchlists only with combined email
python -m wally.main --all-standard-watchlists --combined-email
# Force TII75 inclusion even if gated
python -m wally.main --all-combined --forceThe GitHub Actions workflow now uses combined email mode by default. See docs/WALLY_AND_MASTER_ENGINE_CHANGES.md for details.
Wally reuses Bob email settings where possible:
EMAIL_FROM(orEMAIL_USER)EMAIL_TOEMAIL_APP_PASSWORD(orSMTP_PASS)- optional SMTP overrides:
SMTP_HOST,SMTP_PORT,SMTP_USER
python -m wally.main --watchlist watchlists/jm_watchlist.yaml
python -m wally.main --all-standard-watchlists
python -m wally.main --watchlist watchlists/tii75_watchlist.yaml --force
python -m wally.main --tii75 --forceA new weekly agent is available under sunday-sally/ to review names near 52-week highs and assess valuation stretch vs history.
See sunday-sally/README.md and .github/workflows/sunday_sally_weekly_review.yml.
The Master Engine Alert aggregates events from all agents (Bob, Wally, Ned) into a unified investor briefing with prioritized alerts.
Via GitHub Actions:
- Go to the Actions tab in GitHub
- Select "Master Engine Alert (Super Investor)" workflow
- Click "Run workflow"
- Configure options (include TII75, skip agents, etc.)
- Click "Run workflow" button
Locally:
python run_master_investor.py
python run_master_investor.py --wally-tii75 # Include TII75 watchlist
python run_master_investor.py --no-ned --no-bob # Skip specific agents
python run_master_investor.py --no-email # Generate digest without sending email- Email: Sent to
EMAIL_TOwith subject "Johnny Master Investor Alert — {date} ({N} alert(s))" - Files: Saved in
outputs/YYYY-MM-DD/:master_investor_digest.html- Full HTML digestmaster_investor_digest.md- Markdown summarymaster_investor_events.json- JSON archive
The workflow runs automatically daily at 00:00 UTC, after all individual agent workflows have completed.
See docs/WALLY_AND_MASTER_ENGINE_CHANGES.md for detailed documentation.