PPLayouts

Architecture

System design and data flow

Two-Phase Run Architecture

The system splits market analysis from trade execution. The scan phase is slow and fault-tolerant (LLM calls, weather APIs, news fetches). The execute phase is fast and deterministic (read cached signals, size, trade).

Scan Phase — every 2h at :30
External APIs
Polymarket Gamma API (500 markets)
ECMWF Ensemble (weather)
DDGS / Claude (news/LLM)
Slow: 2-10 min total
Scanner
factory/scanner.py
Run all active strategies
Regex, model, LLM analysis
Produces Signal objects
Cache
signals table (phase='scan')
TTL: 2 hours
signals
cached
in DB
Execute Phase — every 2h at :00
Read cached signals
get_unconsumed_signals()
Dedup by (strategy, market_id, outcome)
Skip stale (>2h) signals
Runner
factory/runner.py (phase=execute)
Kelly sizing + exposure caps
Strategy + window caps
Fast: <30s
Actions
Open positions via PaperBroker
Resolve closed positions
Portfolio snapshot
WhatsApp summary
Mark signals as consumed

Strategy Lifecycle

Every strategy follows a promotion pipeline from idea to live trading.

1. Proposal
improvement/proposals/
Hypothesis + edge type
2. Alert-Only
Generates signals, no trades
Accumulate N alerts
3. Paper Trading
PaperBroker, real signals
Strategy + window caps
4. Live
Real USDC on Polygon
Kill switch available

Promotion Criteria

  • Alert-only → Paper: strategy-specific (e.g. 30 alerts with >55% accuracy)
  • Paper → Live: positive ROI over 10+ closed trades
  • Each strategy defines its own promotion_criteria

Kill / Review Triggers

  • Loss streak review: 10+ consecutive losses triggers WhatsApp alert
  • Review suggests direction flip (signal correct, side wrong?)
  • Manual kill decision — no auto-kill
  • Killed strategies: code removed, positions wind down

Signal Flow

From market data to trade execution, showing how signals are filtered at each stage.

Markets
500 events from Gamma API
+ tag-based fetches (weather)
~1500 sub-markets
Strategy Scan
Each strategy filters markets
by relevance + edge detection
~5-20 raw signals per run
Sizing + Caps
Kelly criterion sizing
Strategy cap ($20-$80)
Window cap ($40-$150)
Reject if below min EV
Execution
PaperBroker / LiveBroker
Dedup (no double-entry)
~1-5 new positions per run

Schedule & Safeguards

Launchd Schedule

:30Scan phase — fetch 500 markets, run all strategies, cache signals
:00Execute phase — read cached signals, size, trade, resolve, notify
09:00Full WhatsApp summary (all strategies, portfolio stats)
every 2hCombined run: fast pass (1000 mkts observations) + slow pass (500 mkts strategies)
every 30mObserver — price snapshots for 1000 markets (no LLM)
every 30mTrade fetcher — CLOB trade data from Polymarket Data API (no auth)

Safeguards

Exposure caps — per-strategy ($20-$80) and per-time-window ($40-$150) limits
Loss streak alerts — 10+ consecutive losses triggers review (not auto-kill)
Dry run guard — dry runs don't consume cached signals
Run lock — prevents overlapping scan or execute runs
Kill switch — scripts/kill_live.py cancels all live orders immediately

Data Model

Core Tables

  • runs — each scan or execute invocation
  • signals — strategy outputs (phase, consumed_by_run_id)
  • trades — open/closed positions with PnL
  • decisions — sizing, cap, skip, review log
  • events — errors, milestones, info
  • portfolio_snapshots — periodic mark-to-market
  • market_observations — price snapshots every 30 min (1000 markets)
  • market_trades — CLOB trade data (deduped by tx_hash)
  • base_rate_index.pkl — TF-IDF kNN index of ~3k resolved markets (scikit-learn)

Key Files

  • factory/scanner.py — scan phase (500 markets)
  • factory/runner.py — combined mode (1000 obs + 500 strategies)
  • factory/observer.py — lightweight price observer (1000 markets)
  • factory/trade_fetcher.py — CLOB trade data pipeline
  • factory/strategies/ — one file per strategy
  • factory/db.py — SQLite ORM + migrations
  • factory/broker.py — Paper + Live broker
  • factory/base_rate_index.py — TF-IDF kNN for base rate estimation
  • factory/feed.py — Gamma API + paginated fetch
  • factory/notify.py — WhatsApp via OpenClaw