feat(polymarket): add Polymarket prediction markets as 6th research source

Search Polymarket's free Gamma API for relevant prediction markets on any
topic. Uses smart multi-query expansion to cast a wider net (e.g., "Arizona
Basketball" also searches "Arizona"), merges and dedupes by event ID, and
shows price movement context ("up 22.5% this week"). No API key required.

Also hides sources with zero results from the stats output (all sources).

54 new tests, all passing. Full pipeline integration with scoring, dedupe,
cross-source linking, and rendering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Van Horn
2026-02-25 22:27:19 -08:00
parent 52503f8e68
commit 994a4ab2ca
17 changed files with 1699 additions and 43 deletions
+21 -1
View File
@@ -79,6 +79,13 @@ HN_MESSAGES = [
"Discovering developer conversations...",
]
POLYMARKET_MESSAGES = [
"Checking prediction markets...",
"Finding what people are betting on...",
"Scanning Polymarket for odds...",
"Discovering prediction markets...",
]
PROCESSING_MESSAGES = [
"Crunching the data...",
"Scoring and ranking...",
@@ -274,6 +281,15 @@ class ProgressDisplay:
if self.spinner:
self.spinner.stop(f"{Colors.YELLOW}HN{Colors.RESET} Found {count} stories")
def start_polymarket(self):
msg = random.choice(POLYMARKET_MESSAGES)
self.spinner = Spinner(f"{Colors.GREEN}Polymarket{Colors.RESET} {msg}", Colors.GREEN, quiet=True)
self.spinner.start()
def end_polymarket(self, count: int):
if self.spinner:
self.spinner.stop(f"{Colors.GREEN}Polymarket{Colors.RESET} Found {count} markets")
def start_processing(self):
msg = random.choice(PROCESSING_MESSAGES)
self.spinner = Spinner(f"{Colors.PURPLE}Processing{Colors.RESET} {msg}", Colors.PURPLE)
@@ -283,7 +299,7 @@ class ProgressDisplay:
if self.spinner:
self.spinner.stop()
def show_complete(self, reddit_count: int, x_count: int, youtube_count: int = 0, hn_count: int = 0):
def show_complete(self, reddit_count: int, x_count: int, youtube_count: int = 0, hn_count: int = 0, pm_count: int = 0):
elapsed = time.time() - self.start_time
if IS_TTY:
sys.stderr.write(f"\n{Colors.GREEN}{Colors.BOLD}✓ Research complete{Colors.RESET} ")
@@ -294,6 +310,8 @@ class ProgressDisplay:
sys.stderr.write(f" {Colors.RED}YouTube:{Colors.RESET} {youtube_count} videos")
if hn_count:
sys.stderr.write(f" {Colors.YELLOW}HN:{Colors.RESET} {hn_count} stories")
if pm_count:
sys.stderr.write(f" {Colors.GREEN}Polymarket:{Colors.RESET} {pm_count} markets")
sys.stderr.write("\n\n")
else:
parts = [f"Reddit: {reddit_count} threads", f"X: {x_count} posts"]
@@ -301,6 +319,8 @@ class ProgressDisplay:
parts.append(f"YouTube: {youtube_count} videos")
if hn_count:
parts.append(f"HN: {hn_count} stories")
if pm_count:
parts.append(f"Polymarket: {pm_count} markets")
sys.stderr.write(f"✓ Research complete ({elapsed:.1f}s) - {', '.join(parts)}\n")
sys.stderr.flush()