How ronnyling/native-ai-auction-investment works
1. This repo is a Python-driven property investment pipeline, not a general app. Its primary job is to collect Malaysian auction listings, enrich them with supporting data, score them for investment potential, and write the result into an Obsidian vault for human review.
2. The core runtime is scraper/main.py, which acts as the daily orchestrator. It runs a multi-stage flow over configured states, optionally in delta mode using known_ids.json, and emits both structured notes and run summaries. A GitHub Actions workflow in .github/workflows/daily-scrape.yml appears to be the scheduled automation entrypoint.
3. The ingestion layer is source-specific. Separate scrapers handle BidNow, e-Lelong, LelongTips, and additional market/source probes for iProperty and PropertyGuru. These scrapers produce raw listing data and, in some cases, cached HTML or JSON fixtures used for debugging and regression.
4. After ingestion, the pipeline normalizes and consolidates listings. dedup_merger.py handles cross-source deduplication and re-auction detection, while bidnow_filter_enums.py and related source modules encode source-specific parsing and filtering rules. This is the main boundary between raw, inconsistent web data and the canonical property model.
5. Enrichment is split into a few distinct services. market_research.py maintains the iProperty PSF cache with TTL behavior, geocode.py adds location data via Nominatim, and analyst_agent.py provides investment scoring with an OpenAI GPT-4o-mini path plus rule-based fallback. These enrichers are optional but materially affect the final recommendation.
6. The investment computation layer is entry_cost.py. It calculates entry costs and projected returns across five scenarios, including flip, rental, room rental, partition rental, and capital hold. This is the main analytical engine that turns a listing into an ROI-style decision artifact.
7. The output boundary is the Obsidian vault under vault/. md_writer.py serializes each property into a Markdown note with YAML frontmatter, and the vault includes dashboards, daily notes, templates, and per-property notes. The vault is the human-facing system of record; the scraper writes into it, while Obsidian provides review, search, and lifecycle management.
8. Property lifecycle state is stored in note metadata and drives prioritization. The canonical statuses move from new to closed, with active states like reviewing, shortlisted, visiting, and bid. Several tools read this status to determine what should be surfaced in daily runs or monitor views.
9. The POS pipeline is a second major subsystem focused on ground-truth legal documents. Modules such as pos_bulk_download.py, pos_identifier.py, pos_parser.py, pdf_extractor.py, and hermes.py download, identify, extract, and enrich Proclamation of Sale PDFs. This subsystem feeds higher-confidence fields like reserve price, tenure, encumbrances, borrower, and full location data back into the property record.
10. There is a clear separation between live scraping and offline regression. audit.py runs broad scenario-based validation, pos_regression.py and related PDF caches exercise the POS parser against a fixed corpus, and validate_parser.py/check_pdfs.py help inspect correctness. This repo treats parser stability as a first-class concern because source layouts vary frequently.
11. Several local JSON caches and stored artifacts support repeatability and resilience: market_cache.json, geocache.json, known_ids.json, pos_study_urls.json, and pos_study_findings.json. These are important diagram nodes because they show that the pipeline is not purely stateless; it depends on persisted working sets to avoid reprocessing and to support offline analysis.
12. The repo also includes a large debugging and probe surface. Files like debug_*, probe_*, inspect_*, vault_scan.py, and e2e_* are operational tooling for troubleshooting live source changes, verifying end-to-end behavior, and testing specific scenarios such as due diligence or state-specific scrapes. These tools sit around the main pipeline rather than in the critical path, but they explain how the system is maintained.
13. The Obsidian integration is not just passive output. The vault contains dashboards, templates, Dataview/Templater plugins, and a cockpit bootstrap path that can trigger a PowerShell script from inside Obsidian. That makes the vault both a destination and an operational control surface for the pipeline.
14. In architecture terms, the main data flow is: live property sources and PDF sources in, normalized listing records and POS-derived facts through enrichment and scoring, then Markdown notes and daily summaries out to the vault. The key boundaries are source adapters, canonical merge/enrichment, investment analysis, and note generation.
Main entry points: main.py Daily run · dedup_merger.py Merge canon · vault Vault · probe_all_sources.py Probe suite
Components 31
Live scrape
Source adapters and daily orchestration for auction listings.
- Daily run · Coordinates the daily scrape across states, delta mode, enrichment, scoring, and note generation.
scraper/main.py - BidNow · Scrapes BidNow listings and source-specific fields.
scraper/bidnow.py - e-Lelong · Scrapes e-Lelong auction listings.
scraper/elelong.py - LelongTips · Scrapes LelongTips listings and related source data.
scraper/lelongtips.py - PropertyGuru · Probes PropertyGuru as an auxiliary source boundary.
scraper/propertyguru.py - iProperty · Probes iProperty and feeds market-research caches.
scraper/iproperty.py
Normalize & enrich
Canonical merge, geocoding, market research, and scoring.
- Merge canon · Deduplicates cross-source listings and detects re-auctions.
scraper/dedup_merger.py - BidNow rules · Encodes BidNow enum mapping and source-specific filtering behavior.
scraper/bidnow_filter_enums.py - Market research · Maintains iProperty PSF cache with TTL and probe support.
scraper/market_research.py - Geocode · Adds location data using Nominatim and caches results.
scraper/geocode.py - Analyst · Scores listings with GPT-4o-mini and rule-based fallback.
scraper/analyst_agent.py - Entry cost · Computes entry costs and projected returns across flip, rental, room rental, partition rental, and hold scenarios.
scraper/entry_cost.py
POS pipeline
Proclamation of Sale PDF ingestion and fact extraction.
- POS download · Downloads Proclamation of Sale PDFs for study and extraction.
scraper/pos_bulk_download.py - POS identify · Identifies the correct POS document for a property.
scraper/pos_identifier.py - POS parser · Parses POS PDFs into structured legal and auction facts.
scraper/pos_parser.py - PDF extract · Extracts text and layout signals from PDFs for downstream parsing.
scraper/pdf_extractor.py - Hermes · Enriches POS-derived facts back into the property record.
scraper/hermes.py
Obsidian vault
Human review surface, dashboards, templates, and generated notes.
- Note writer · Serializes canonical property records into Markdown with YAML frontmatter.
scraper/md_writer.py - Vault · Human-facing record system containing property notes, dashboards, daily notes, and templates.
vault - Templates · Startup, daily note, and property note templates used by the vault.
vault/_templates - Daily notes · Daily summaries and review pages generated for human triage.
vault/Daily Notes - Property notes · Per-property Markdown records used for review and lifecycle management.
vault/Properties
Ops & validation
Scheduled automation, probes, regression, and debugging tools.
- Schedule · Scheduled automation entrypoint for the daily scrape.
.github/workflows/daily-scrape.yml - Audit · Runs scenario-based validation across the pipeline.
scraper/audit.py - POS regression · Replays the POS parser against a fixed corpus and caches.
scraper/pos_regression.py - Debug tools · Operational probes, inspection tools, and failure analysis helpers.
scraper/debug_cockpit.py - Probe suite · Live probes and end-to-end checks around individual sources and combined flows.
scraper/probe_all_sources.py
Persistent state
Local caches and working sets used for repeatability.
- Known IDs · Persistent working set used for delta runs and dedup-aware scraping.
scraper/known_ids.json - Market cache · Stored PSF and market lookup cache with probe artifacts.
scraper/market_cache.json - Geocache · Stored geocoding results used to avoid repeated lookups.
scraper/geocache.json - POS corpus · Stored POS study PDFs and findings used for repeatable parser analysis.
scraper/pos_study_cache
Connections 31
- Schedule → Daily run: scheduled run · Triggers the daily orchestration on a schedule.
- Daily run → BidNow: collect · Pulls source-specific auction listings.
- Daily run → e-Lelong: collect · Pulls source-specific auction listings.
- Daily run → LelongTips: collect · Pulls source-specific auction listings.
- Daily run → PropertyGuru: probe · Uses market probes as auxiliary inputs.
- Daily run → iProperty: probe · Uses market probes as auxiliary inputs.
- Daily run → Merge canon: merge · Normalizes and consolidates raw listings.
- BidNow → BidNow rules: apply rules · Depends on source-specific parsing and filtering rules.
- Merge canon → Market research: enrich · Feeds listings into market research lookups.
- Merge canon → Geocode: enrich · Adds location data to canonical listings.
- Merge canon → Analyst: score · Passes canonical listings to investment scoring.
- Merge canon → Entry cost: analyze · Computes entry costs and projected returns.
- Market research → Market cache: persist · Reads and updates the PSF cache.
- Geocode → Geocache: persist · Reads and updates geocoding cache.
- POS download → POS identify: identify · Locates the relevant POS document for a listing.
- POS identify → POS parser: parse · Hands matched documents to the parser.
- POS parser → PDF extract: extract · Uses PDF extraction as a parsing input.
- POS parser → Hermes: enrich · Converts parsed POS fields into record-level facts.
- Hermes → Merge canon: merge facts · Feeds POS-derived fields back into the canonical property model.
- Daily run → Note writer: write notes · Serializes final property records and run artifacts.
- Note writer → Vault: store · Writes Markdown notes into the vault.
- Vault → Daily notes: contains · Organizes daily review notes and summaries.
- Vault → Property notes: contains · Organizes per-property notes.
- Vault → Templates: contains · Holds Obsidian templates and startup scaffolding.
- Vault → Known IDs: status state · Uses note metadata and status to prioritize work.
- Audit → POS corpus: exercise · Validates parser behavior against stored POS corpora.
- POS regression → POS corpus: exercise · Runs fixed-corpus regression against cached PDFs.
- Probe suite → BidNow: inspect · Probes live source behavior and end-to-end flows.
- Probe suite → e-Lelong: inspect · Probes live source behavior and end-to-end flows.
- Probe suite → iProperty: inspect · Probes live source behavior and end-to-end flows.
- Debug tools → Daily run: troubleshoot · Supports operational debugging of the main flow.