Skip to main content

Architecture Guide

System Overview

┌─────────────────────────────────────────────────────────────┐
│ Frontend (Next.js 16) │
│ Dashboard | Real-time ticker | Edge leaderboard | Position │
│ | Backtest runner | Research reports │
└──────────────────────────┬──────────────────────────────────┘
│ WebSocket + REST
┌──────────────────────────▼──────────────────────────────────┐
│ FastAPI Backend │
│ ├─ Market Data Service │
│ │ └─ Gamma API polling (1s), order book, OHLCV │
│ ├─ Edge Detection Service │
│ │ └─ Fair probability vs market price │
│ ├─ Inference Engine │
│ │ └─ Bayesian probability + ensemble models │
│ ├─ Portfolio Management │
│ │ └─ Position tracking, correlation, risk │
│ ├─ Order Management │
│ │ └─ Entry, laddering, execution simulation │
│ └─ Research Service │
│ └─ Semantic search, embeddings, reports │
└──────────────────────────┬──────────────────────────────────┘

┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ Gamma │ │ NewsAPI│ │ Twitter│
│ API │ │ │ │ API │
└────────┘ └────────┘ └────────┘


├─────────────────────────────────────────┐
│ │
┌───▼────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│Postgres│ │TimescaleDB │ Chroma │ │ Redis │
│Positions │OHLCV, order │Embeddings │Cache, │
│Orders │ │book snapshots │Vector DB │ │Alerts │
│Trades │ └──────────────┘└──────────┘ └──────────┘
└────────┘

Core Services

1. Market Data Service

File: src/polysuggest/market_data.py

Async polling of Gamma API for real-time market data.

class MarketDataPoller:
async def fetch_all() -> List[Market]
# Returns: {id, bid, ask, yes_price, no_price, volume, spread_pct, timestamp}

async def subscribe(market_id: str, callback: Callable)
# Stream updates to callback (WebSocket handler)

Storage: TimescaleDB

  • Table: market_prices (timestamp, market_id, bid, ask, yes_price, volume, spread_pct)
  • Index: (market_id, timestamp DESC) for fast lookups

2. Edge Detection Service

File: src/polysuggest/edge_detector.py

Continuously compares fair probability (model estimate) vs market probability.

class EdgeDetector:
async def detect_edges() -> List[Edge]
# For each market:
# fair_prob = inference_engine.estimate(market)
# market_prob = market.yes_price
# edge_pct = (fair_prob / market_prob) - 1
# if |edge_pct| > threshold: emit alert

def _decay_score(last_update: datetime) -> float
# Older edges decay; fresh edges score 1.0

Output: Edges ranked by EV × Liquidity × Confidence

3. Inference Engine (Bayesian)

File: src/polysuggest/inference_engine.py

Produces calibrated probability estimates using Bayes theorem.

class BayesianInferenceEngine:
def estimate_fair_probability(market) -> Tuple[float, Tuple[float, float]]
# Inputs:
# - market.category (prior from base_rates)
# - market.trend_sentiment (likelihood)
# - market.order_flow_5m (likelihood)
#
# Outputs:
# - fair_probability (0.55)
# - credible_interval ((0.47, 0.63))

Dependencies:

  • PyMC3 or Pyro for MCMC sampling
  • base_rates.py for historical priors by market type
  • Training data: 12 months of historical Polymarket resolutions

4. Portfolio Tracker

File: src/polysuggest/portfolio_tracker.py

Real-time tracking of user positions and P&L.

class PortfolioTracker:
def add_position(user_id, market_id, outcome, quantity, entry_price)
def close_position(position_id, exit_price)
def get_pnl(user_id) -> Dict
# Returns: realized_pnl, unrealized_pnl, total, win_rate
def get_portfolio_heat() -> float
# Max loss as % of bankroll (all correlated scenarios)

Storage: PostgreSQL

  • Table: positions (user_id, market_id, outcome, quantity, entry_price, exit_price, pnl)
  • Table: orders (user_id, market_id, outcome, size, limit_price, status, fill_price)

5. Risk Manager

File: src/polysuggest/risk_manager.py

Correlation analysis, VaR calculation, liquidation risk.

class PortfolioRiskManager:
def compute_portfolio_heat() -> Dict
# max_loss, total_exposure, liquidation_risk, correlation_matrix
def hedge_suggestion(new_position) -> Optional[str]
# If new position is correlated to existing, suggest a short hedge

6. Order Manager

File: src/polysuggest/order_manager.py

Order lifecycle: entry, execution simulation, fill tracking.

class OrderManager:
def place_order(market_id, outcome, size, limit_price, user_id) -> Order
def ladder_order(order_id, num_tranches: int, time_window: timedelta)
# Splits order across time for better execution
def estimate_slippage(size, liquidity, spread) -> float
# What will this order actually fill at?

7. Backtester

File: src/polysuggest/backtester.py

Historical simulation of edge detection and execution.

class Backtester:
def backtest(start_date, end_date, strategy) -> BacktestResult
# Replay historical market data
# Generate edges as of each timestamp
# Simulate executions at historical prices
# Compute realized P&L, Sharpe, max drawdown

Data Models

Market

@dataclass
class Market:
id: str
title: str
category: str # "crypto", "sports", "geopolitical", "economics"
yes_price: float
no_price: float
bid: float
ask: float
volume: float
spread_pct: float
liquidity: float
trend_sentiment: float # -1.0 to +1.0 from trend scanner
last_update: datetime

Edge

@dataclass
class Edge:
market_id: str
fair_prob: float # Model estimate
market_prob: float # Current YES price
edge_pct: float # (fair_prob / market_prob) - 1
liquidity: float
trend_sentiment: float
confidence: float # 0-1, based on freshness + liquidity
decay_score: float # 1.0 → 0.0 as it ages
detected_at: datetime

Position

@dataclass
class Position:
id: str
user_id: str
market_id: str
outcome: str # "YES" or "NO"
quantity: int
entry_price: float
entry_date: datetime
exit_price: Optional[float]
closed_at: Optional[datetime]
pnl: Optional[float]

Suggestion

@dataclass
class Suggestion:
id: str
trend: str
suggested_outcome: str # "YES" or "NO"
confidence: float # 0-1
reasoning: str
created_at: datetime
markets: List[str] # market IDs it applies to
resolved: bool
actual_outcome: Optional[str]
calibration_error: Optional[float]

Database Schema

PostgreSQL

-- Positions
CREATE TABLE positions (
id BIGINT PRIMARY KEY,
user_id TEXT NOT NULL,
market_id TEXT NOT NULL,
outcome TEXT NOT NULL, -- 'YES' or 'NO'
quantity INT NOT NULL,
entry_price FLOAT NOT NULL,
entry_date TIMESTAMP NOT NULL,
closed_at TIMESTAMP,
exit_price FLOAT,
pnl FLOAT,
created_at TIMESTAMP DEFAULT NOW()
);

-- Orders
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id TEXT NOT NULL,
market_id TEXT NOT NULL,
outcome TEXT NOT NULL,
size INT NOT NULL,
limit_price FLOAT NOT NULL,
created_at TIMESTAMP NOT NULL,
filled_at TIMESTAMP,
fill_price FLOAT,
status TEXT, -- 'pending', 'filled', 'cancelled'
slippage FLOAT
);

-- Suggestions + Calibration
CREATE TABLE suggestions (
id BIGINT PRIMARY KEY,
trend TEXT NOT NULL,
suggested_outcome TEXT NOT NULL,
confidence FLOAT NOT NULL,
reasoning TEXT,
created_at TIMESTAMP NOT NULL,
resolved_at TIMESTAMP,
actual_outcome TEXT,
calibration_error FLOAT
);

-- Edges (detected mismatches)
CREATE TABLE edges (
id BIGINT PRIMARY KEY,
market_id TEXT NOT NULL,
detected_at TIMESTAMP NOT NULL,
fair_probability FLOAT NOT NULL,
market_probability FLOAT NOT NULL,
edge_pct FLOAT NOT NULL,
liquidity_available FLOAT NOT NULL,
trend_sentiment FLOAT NOT NULL,
decay_score FLOAT NOT NULL,
resolved_at TIMESTAMP,
actual_outcome TEXT,
pnl FLOAT
);

CREATE INDEX idx_positions_user_market ON positions(user_id, market_id);
CREATE INDEX idx_edges_market_detected ON edges(market_id, detected_at DESC);
CREATE INDEX idx_suggestions_created ON suggestions(created_at DESC);

TimescaleDB (Hypertable)

CREATE TABLE market_prices (
time TIMESTAMP NOT NULL,
market_id TEXT NOT NULL,
bid FLOAT,
ask FLOAT,
yes_price FLOAT,
no_price FLOAT,
volume FLOAT,
spread_pct FLOAT
);

SELECT create_hypertable('market_prices', 'time', if_not_exists => TRUE);
CREATE INDEX idx_market_prices ON market_prices (market_id, time DESC);

API Endpoints (FastAPI)

Markets

  • GET /api/markets – List all markets with current prices
  • GET /api/markets/{id} – Get market details + recent prices
  • GET /api/markets/{id}/history – OHLCV data (1m, 5m, 1h)

Edges

  • GET /api/edges – All detected edges
  • GET /api/edges?top=10 – Top 10 by EV
  • WS /api/edges/stream – WebSocket for real-time edge alerts

Portfolio

  • GET /api/portfolio/positions – User's open positions
  • GET /api/portfolio/pnl – P&L summary
  • GET /api/portfolio/risk – Portfolio heat, correlation, VaR
  • POST /api/portfolio/hedge-suggestion – Get hedge recommendations

Orders

  • POST /api/orders/place – Place a new order
  • POST /api/orders/{id}/ladder – Ladder an order
  • GET /api/orders/{id} – Get order status + fills

Backtester

  • POST /api/backtest – Run a backtest
  • GET /api/backtest/{id} – Get backtest results

Deployment

Local Development

# Start databases
docker-compose up postgres timescaledb chroma redis

# Run FastAPI server
uvicorn src.polysuggest.api.main:app --reload

# Run Celery worker
celery -A src.polysuggest.tasks worker --loglevel=info

# Run frontend
cd polysuggest && npm run dev

Production

  • API: Cloud Run / Heroku / EC2
  • Databases: AWS RDS (PostgreSQL), AWS Timestream or managed TimescaleDB
  • Frontend: Vercel / Netlify
  • Task Queue: AWS SQS / ECS / Celery on EC2
  • Cache: ElastiCache (Redis)

Monitoring & Observability

  • Logging: Loguru (Python), Pino (Node.js)
  • Metrics: Prometheus + Grafana
  • Tracing: OpenTelemetry (to DataDog / Jaeger)
  • Alerts: PagerDuty (API errors, data staleness)