Skip to content

Repository files navigation

⚡ ArbOS

Automated Prediction Market Arbitrage Engine

The market is wrong. Profit from it.

ArbOS is a low-latency, logic-driven trading system that automatically detects and exploits mathematical inconsistencies in decentralized prediction markets on Polymarket (Polygon PoS).

CIRustPythonTypeScriptIBM watsonxLicense


Table of Contents


What is ArbOS?

Prediction markets like Polymarket let people trade the probability of real-world events. Each market has its own isolated order book, and nobody enforces consistency across related markets. That structural gap is the alpha.

Unlike traditional crypto arbitrage (buy low on Exchange A, sell high on Exchange B), ArbOS performs Logical Arbitrage entirely within Polymarket — it finds situations where the math of the order books breaks the rules of logic, then executes delta-neutral trades to lock in a mathematically guaranteed, risk-free profit.

ArbOS is a polyglot monorepo spanning three languages: Rust for the microsecond-sensitive data and execution path, Python for the AI reasoning layer, and TypeScript/React for the real-time control dashboard — unified by a PostgreSQL data plane and an IBM watsonx.ai reasoning core.

Built for the IBM SkillsBuild Hackathon · Fintech Track · February 2026.

MetricValue
Arbitrage detection latency~14 ms end-to-end
Simultaneous markets trackedup to 1,000 live order books
Arbitrage strategies4 (Implication, Partition, Mutual Exclusion, AI-Chained)
Minimum profit gate$0.50 net, after fees + gas
Order safety modelFill-or-Kill only, with atomic panic-dump failsafe
Reasoning engineIBM watsonx.ai (mistralai/mistral-large) via LangGraph

The Alpha: Logical Arbitrage

Consider two markets:

  1. Market AWill the Fed cut rates in June 2026? → trading at 60¢ (60% implied)
  2. Market BWill the Fed cut rates in 2026? → trading at 55¢ (55% implied)

June 2026 is a strict subset of the year 2026. It is logically impossible for the Fed to cut in June without also having cut in 2026. Therefore P(A) can never exceed P(B) — yet the market prices A higher than B. The order books have contradicted the laws of logic.

flowchart LR
A["<b>Market A</b><br/>Fed cut in June 2026<br/><b>60¢</b>"]
B["<b>Market B</b><br/>Fed cut in 2026<br/><b>55¢</b>"]
A -- "A ⊆ B, so P(A) ≤ P(B)<br/>VIOLATION: 60¢ > 55¢" --> B
A -. "SELL @ 60¢" .-> S["🔒 Locked-in profit<br/><b>+5¢ / share</b><br/>(risk-free)"]
B -. "BUY @ 55¢" .-> S
classDef mkt fill:#16213E,stroke:#00D4AA,color:#F8FAFC;
classDef win fill:#0A3D2E,stroke:#00FF88,color:#F8FAFC;
class A,B mkt;
class S win;
Loading

Why it's guaranteed, not merely probable: a cross-exchange spread can close against you before you execute. A logical violation cannot — if June ⊆ 2026, that is true regardless of what the Fed actually does. The profit is locked in the moment both legs fill.


The Four Arbitrage Strategies

#StrategyAxiomViolationAction
1Implication (Monotonicity)If A ⊆ B, then P(A) ≤ P(B)P(A)_bid > P(B)_ask + feesSELL sub-event A, BUY super-event B
2Partition (Normalization)Σ P(Eᵢ) = 1.0Σ P(Eᵢ)_bid > 1.0 + feesBasket-SELL every outcome
3Mutual Exclusion (Contradiction)P(A) + P(B) ≤ 1.0P(A)_bid + P(B)_bid > 1.0SELL both sides
4Chained Implication (AI-discovered)If A → B → C, then P(A) ≤ P(C)P(A) > P(C)Multi-hop; mapped by the AI Brain

Worked example — Implication arb (Fed Cut June 60¢ → Fed Cut 2026 55¢, 100 shares):

SELL June YES @ 60¢ = $60.00 collected
BUY 2026 YES @ 55¢ = $55.00 paid
────────────────────────────────────────
Gross spread: +$5.00
Polymarket taker fee: $0.00 (0% on most markets)
Polygon gas (2 txns): -$0.06
────────────────────────────────────────
Net profit: +$4.94 (≈8.2% ROI on $60 risked)

System Architecture

ArbOS is organized into five tiers, each with a single clear responsibility. The three Rust tiers compile into one high-performance orchestrator binary; the Brain runs as an independent Python microservice; the dashboard is a static SPA.

flowchart TB
subgraph EXT["External Services"]
GAMMA["Polymarket Gamma API<br/>(REST · market metadata)"]
CLOB["Polymarket CLOB<br/>(WebSocket · order books)"]
WX["IBM watsonx.ai<br/>(mistral-large LLM)"]
GAS["Polygon Gas Station<br/>(gas oracle)"]
end
subgraph BRAIN["🧠 Tier 2 · Brain (Python / FastAPI)"]
SCHED["APScheduler<br/>(15-min discovery)"]
LG["LangGraph pipeline<br/>validate → classify → persist"]
NX["NetworkX DiGraph"]
PG[("PostgreSQL 15<br/>markets · relationships")]
end
subgraph RUST["🦀 Rust Orchestrator (single binary · crates/bot)"]
ING["Tier 1 · Ingestor<br/>WebSocket feed + VWAP"]
ENG["Tier 3 · Engine<br/>arb math + signals"]
EXEC["Tier 4 · Executor<br/>rate-limit · dedup · FOK"]
LEDGER["Ledger<br/>P&L · positions"]
API["Axum Server :3001<br/>REST + WebSocket"]
end
UI["🖥️ Tier 5 · Dashboard<br/>(React / Vite / TS)"]
GAMMA --> SCHED
WX --> LG
SCHED --> LG --> PG
LG --> NX
PG -- "GET /state (every 60s)" --> ENG
CLOB --> ING
GAS --> ENG
ENG -- "Subscribe / Unsubscribe" --> ING
ING -- "NormalizedOrderbook" --> ENG
ENG -- "ArbSignal" --> EXEC
EXEC --> LEDGER
EXEC -- "ExecutionReport (broadcast)" --> API
API -- "WebSocket + /api/status" --> UI
classDef ext fill:#1a1a2e,stroke:#7B61FF,color:#F8FAFC;
classDef brain fill:#16213E,stroke:#00D4AA,color:#F8FAFC;
classDef rust fill:#2A1A0E,stroke:#DEA584,color:#F8FAFC;
classDef ui fill:#0A2540,stroke:#61DAFB,color:#F8FAFC;
class GAMMA,CLOB,WX,GAS ext;
class SCHED,LG,NX,PG brain;
class ING,ENG,EXEC,LEDGER,API rust;
class UI ui;
Loading

Two independent control loops

  • Brain discovery loop (15-min cadence): APScheduler syncs the latest markets from Gamma, then runs the LLM over all unprocessed pairs to grow the relationship graph.
  • Engine execution loop (60-s cadence): the Rust Engine re-polls /state, diffs the tracked-asset set, dynamically manages live WebSocket subscriptions, and refreshes the gas oracle — all while continuously evaluating incoming order-book updates in real time.

How It Works: End-to-End Flow

sequenceDiagram
autonumber
participant G as Gamma API
participant B as Brain (Python)
participant W as watsonx.ai
participant E as Engine (Rust)
participant I as Ingestor (Rust)
participant C as Polymarket CLOB
participant X as Executor (Rust)
participant U as Dashboard
Note over B: Discovery loop — every 15 min
B->>G: fetch active markets (volume ≥ 1000)
G-->>B: market list
B->>W: classify each market pair
W-->>B: IMPLIES / MUT_EXCL / INDEPENDENT + confidence
B->>B: persist relationships → Postgres + NetworkX
Note over E: Execution loop — every 60 s
E->>B: GET /state (X-API-Key)
B-->>E: implications, partitions, contradictions, timestamps
E->>I: Subscribe(asset_ids)
I->>C: subscribe order books (WebSocket)
loop Real-time
C-->>I: order book update
I->>I: compute VWAP to $100 depth
I-->>E: NormalizedOrderbook
E->>E: run implication / partition / contradiction checks
alt Violation & net profit > $0.50
E-->>X: ArbSignal (delta-neutral legs)
X->>X: rate-limit + dedup
X->>C: place Fill-or-Kill orders
C-->>X: fills
X-->>U: ExecutionReport (WebSocket broadcast)
end
end
Loading

The Five Tiers

🦀 Tier 1 — Ingestor (crates/ingestor)

Maintains a real-time, normalized view of the market. A Tokio actor connects to Polymarket's CLOB WebSocket via the official polymarket-client-sdk, dynamically subscribes/unsubscribes to per-asset order books (capped at 1,000), and computes VWAP up to a $100 liquidity target. Books that can't supply $100 of depth are flagged UNTRADEABLE. Reconnects with exponential backoff.

🧠 Tier 2 — Brain (services/brain)

Discovers the "Hidden Graph" of logical relationships between markets. A FastAPI service that uses IBM watsonx.ai (mistralai/mistral-large) orchestrated by a LangGraphStateGraph (validate → classify → persist) to classify each market pair as IMPLIES / MUTUALLY_EXCLUSIVE / INDEPENDENT with a confidence score and reasoning. Relationships persist to PostgreSQL (SQLModel + asyncpg) and an in-memory NetworkX DiGraph. An APScheduler job discovers new relationships every 15 minutes. Exposes /state — the asset-level graph payload the Rust Engine consumes.

🦀 Tier 3 — Engine (crates/engine)

The high-frequency calculator. Caches the entire logical graph in memory, polls the Brain's /state every 60 s (trusting only edges ≥ 0.90 confidence), and runs all three arbitrage strategies on every order-book update. Models taker fees (1.56% conservative) and live Polygon gas (with a 50% safety margin), firing an ArbSignal only when net profit clears $0.50. Enforces a max-duration filter (skips markets resolving > 30 days out).

🦀 Tier 4 — Bot / Executor (crates/bot)

The primary orchestrator entry point and atomic settlement layer. main.rs spawns all Rust tiers as Tokio tasks wired by MPSC channels. The executor applies a token-bucket rate limiter (10 req/s), canonical-hash deduplication (30 s cooldown), and places Fill-or-Kill orders via the Polymarket SDK — with automatic panic-dump of a filled leg if its pair fails. An in-memory ledger tracks P&L and positions; an Axum server exposes REST + WebSocket endpoints to the dashboard. Supports Demo (paper-trading) and Live modes.

🖥️ Tier 5 — Dashboard (frontend)

A React 18 + Vite + TypeScript SPA styled with Tailwind CSS and shadcn/ui. Features a d3-force relationship graph, an AI chat graph-builder, a live control room (P&L ticker, trade log, open positions, system health), and a risk-configuration wizard. Guided operator flow: Landing → Connect → Graph → Configure → Dashboard → Guide.

🦀 Tier 0 — Core (crates/core)

Shared Rust foundation: canonical domain types (ArbSignal, NormalizedOrderbook, ExecutionReport, TradeAction) and tuning constants (fees, gas model, thresholds, channel buffer sizes). All money math uses rust_decimal fixed-point — never floats.


Tech Stack

mindmap
root((ArbOS))
Rust
Tokio async runtime
Axum web/WebSocket
polymarket-client-sdk
rust_decimal
reqwest
tracing
Python
FastAPI
LangGraph + LangChain
langchain-ibm watsonx.ai
SQLModel + asyncpg
NetworkX
APScheduler
loguru
TypeScript
React 18 + Vite
shadcn/ui + Radix
TanStack Query
d3 + Recharts
Tailwind CSS
framer-motion
Platform
Polymarket CLOB + Gamma
Polygon PoS
PostgreSQL 15
Docker + Compose
GitHub Actions CI
Loading
LayerTechnologies
LanguagesRust (edition 2024, toolchain 1.93), Python ≥ 3.13, TypeScript 5.8
Rust runtime & webTokio 1.49, Axum 0.8, tower-http, tokio-util, futures
Blockchain / marketpolymarket-client-sdk 0.4.2 (CLOB WS + EIP-712 signing), reqwest 0.12
AI / LLMIBM watsonx.ai (mistralai/mistral-large), LangGraph 1.0, LangChain-Core 1.2, langchain-ibm 1.0
Python web/dataFastAPI 0.133, Uvicorn 0.41, SQLModel 0.0.37, asyncpg 0.31, Pydantic 2.12, NetworkX 3.6, APScheduler 3.11, httpx 0.28
FrontendReact 18.3, Vite 5.4, TanStack Query 5.83, Radix/shadcn, d3 7.8, Recharts 2.15, Tailwind 3.4, framer-motion 11, zod 3.25
DatabasePostgreSQL 15
Package managersCargo (Rust), uv (Python), npm (frontend)
Testingcargo test, pytest + pytest-asyncio, Vitest + Testing Library
Toolingrustfmt, clippy, ruff, ESLint, pre-commit, Dependabot
Infra & CI/CDDocker + Docker Compose, GitHub Actions

Getting Started

Prerequisites

  • Docker & Docker Compose (for the full-stack launch)
  • Rust 1.93+ with Cargo (for Rust development)
  • Python 3.13+ with uv (for the Brain)
  • Node.js 18+ with npm (for the frontend)
  • Credentials: an IBM watsonx.ai API key + project ID, and (for live trading) Polymarket API key / passphrase / secret

Quick start (Docker Compose)

# 1. Clone
git clone git@github.com:Harikeshav-R/Arb-OS.git
cd Arb-OS
# 2. Configure — copy the template and fill in your keys
cp .env.example .env
# edit .env → WATSONX_APIKEY, WATSONX_PROJECT_ID, POLYMARKET_* , etc.# 3. Launch the stack (Postgres + Brain + Ingestor + Frontend)
make up # or: docker compose up -d# 4. Watch it run
make ps # container status
make logs # tail all logs (make logs S=brain for one service)
ServiceURL
Brain API (FastAPI)http://localhost:8000 · docs at /docs
Frontend dashboardhttp://localhost:5173
Bot API + WebSockethttp://localhost:3001
PostgreSQLpostgresql://postgres:password@localhost:5432/arbos

Common Make targets

make up # start all services in the background
make down # stop and remove containers
make build # rebuild images and start
make logs # tail logs (S=<service> for one)
make db # open psql against the database
make test# run all tests (Rust + Brain)
make clean # stop and remove volumes (⚠ deletes DB data)

Running tiers individually

# Rust orchestrator (spawns Ingestor + Engine + Executor + API server)
cargo run -p bot
# Brain (Python / FastAPI)cd services/brain && uv run uvicorn main:app --reload
# Frontend (Vite dev server)cd frontend && npm install && npm run dev

Configuration

All configuration is via environment variables (see .env.example).

Polymarket & infrastructure

VariableDescription
POLYMARKET_API_KEY / PASSPHRASE / SECRETCLOB API credentials (required for live trading)
DATABASE_URLPostgreSQL DSN (postgresql://…)
REDIS_URLReserved for future cross-service signal bus

IBM watsonx.ai (Brain / Tier 2)

VariableDefaultDescription
WATSONX_APIKEYwatsonx.ai API key (required)
WATSONX_URLhttps://us-south.ml.cloud.ibm.comwatsonx.ai region endpoint
WATSONX_PROJECT_IDwatsonx.ai project ID (required)
WATSONX_MODEL_IDmistralai/mistral-largeLLM model

Brain tuning

VariableDefaultDescription
BRAIN_LLM_TEMPERATURE0.0LLM sampling temperature
BRAIN_LLM_MAX_TOKENS1024Max tokens per LLM call
BRAIN_CONFIDENCE_THRESHOLD0.7Min confidence to persist a relationship
BRAIN_GAMMA_API_BASE_URLhttps://gamma-api.polymarket.comGamma API base
BRAIN_GAMMA_MIN_VOLUME1000Min market volume to track
BRAIN_GAMMA_FETCH_LIMIT100Markets fetched per page

Bot / Engine (Rust)

VariableDefaultDescription
ARBOS_LIVE_MODEfalsefalse → Demo (paper); true → live execution
ARBOS_INITIAL_ASSETSCSV of asset IDs to seed subscriptions
ARBOS_WS_PORT3001Bot REST + WebSocket port
ARBOS_DEDUP_COOLDOWN30Signal dedup cooldown (seconds)
ARBOS_MAX_HISTORY500Ledger trade-history ring-buffer size
BRAIN_API_URLhttp://localhost:8000Engine → Brain endpoint
ADMIN_API_KEYX-API-Key for protected Brain endpoints
VITE_ALLOWED_ORIGINShttp://localhost:5173CORS allow-list for the Bot API

Key thresholds (crates/core/src/constants.rs): TARGET_LIQUIDITY = $100, MIN_PROFIT_THRESH_USDC = $0.50, MIN_CONFIDENCE_THRESHOLD = 0.90, MAX_TRACKED_ASSETS = 1000, EXECUTOR_RATE_LIMIT_PER_SEC = 10. Note the dual confidence gate: the Brain persists edges at ≥ 0.70 but the Engine only trades edges at ≥ 0.90.


API Reference

Brain (FastAPI · :8000)

MethodPathAuthPurpose
GET/healthDB + LLM configuration status
POST/markets/syncadminUpsert markets from Gamma
GET/marketsList cached markets (paginated)
POST/analyzeAnalyze a single market pair
POST/scanadminScan top-N markets, analyze all unprocessed pairs
GET/relationshipsList active relationships (filter by logic_type)
GET/relationships/{condition_id}Relationships involving a market
GET/graph/statsGraph summary (edge/component counts)
GET/stateadminFull asset-level graph payload for the Engine

Admin endpoints require an X-API-Key header when ADMIN_API_KEY is set.

Bot (Axum · :3001)

MethodPathPurpose
GET/api/healthLiveness probe
GET/api/statusJSON snapshot: mode, cumulative P&L, signals executed, uptime, open positions, recent trades
GET/wsWebSocket — sends a SNAPSHOT on connect, then streams live EXECUTION_REPORT messages

Project Structure

Arb-OS/
├── Cargo.toml # Rust workspace: core, ingestor, engine, bot
├── docker-compose.yml # postgres + brain + ingestor + frontend
├── Makefile # Docker lifecycle + test targets
├── .env.example # All environment variables
├── PROJECT.md # Architecture & theory spec
├── REPORT.md # Detailed engineering report
├── AGENTS.md # AI-agent contributor guide
│
├── crates/ # ── RUST WORKSPACE ──
│ ├── core/ # Tier 0: shared domain types + constants
│ ├── ingestor/ # Tier 1: WebSocket feed + VWAP normalization
│ │ └── src/{main,actor,clob_client}.rs
│ ├── engine/ # Tier 3: strategy math + signal generation
│ │ └── src/{main,actor}.rs, strategies/{implication,partition,contradiction}.rs
│ └── bot/ # Tier 4: orchestrator + executor + server
│ └── src/{main,config,executor,ledger,dedup,server}.rs
│
├── services/
│ └── brain/ # ── PYTHON (Tier 2: AI) ──
│ ├── main.py # FastAPI app + APScheduler + endpoints
│ ├── graph.py # LangGraph pipeline (validate→classify→persist)
│ ├── gamma_client.py # Polymarket Gamma API client
│ ├── relationship_graph.py # NetworkX DiGraph manager
│ ├── models.py # SQLModel tables + API schemas
│ ├── database.py # Async SQLAlchemy/asyncpg engine
│ ├── config.py # Pydantic settings
│ ├── prompts.py # LLM prompt templates
│ └── tests/ # pytest suite
│
├── frontend/ # ── REACT/VITE/TS (Tier 5: Dashboard) ──
│ └── src/
│ ├── App.tsx # Router: /, /connect, /graph, /configure, /dashboard, /guide
│ ├── pages/ # Index, Connect, Graph, Configure, Dashboard, Guide
│ ├── components/ # ForceGraph, Navbar, WebGLCanvas + shadcn/ui
│ └── hooks/
│
└── .github/workflows/ci.yml # 3 CI jobs: rust-check, python-check, frontend-check

Development

Testing

make test# everything
make test-rust # cargo test (engine, ingestor, core)
make test-brain # pytest suite for the Braincd frontend && npm test# Vitest

Linting & formatting

cargo fmt --all && cargo clippy --all-targets --all-features -- -D warnings
cd services/brain && uv run ruff check .cd frontend && npm run lint

Pre-commit hooks (.pre-commit-config.yaml) run rustfmt, clippy, ruff, and the frontend lint/typecheck automatically:

pip install pre-commit && pre-commit install

CI pipeline

Every push/PR to main runs three parallel GitHub Actions jobs:

flowchart LR
P["push / PR → main"] --> R["rust-check<br/>fmt · clippy -D warnings · cargo test"]
P --> Y["python-check<br/>uv sync · ruff · pytest"]
P --> F["frontend-check<br/>npm ci · tsc --noEmit"]
R --> M{"all green?"}
Y --> M
F --> M
M -->|yes| OK["✅ mergeable"]
classDef job fill:#16213E,stroke:#00D4AA,color:#F8FAFC;
class R,Y,F job;
Loading

Git workflow

ArbOS uses Conventional Commits with scoped branches (type/scope/description):

  • Types:feat/, fix/, chore/, refactor/
  • Scopes:engine, ingestor, bot, brain, web, infra

Example: feat(engine): add VWAP calculator · fix(brain): gamma-api filter


Safety & Risk Engineering

Guaranteed-at-resolution does not mean guaranteed-before-resolution. ArbOS hardens against every real-world failure mode:

HazardDefense
Single-leg exposurePanic-dump: close the filled leg immediately; pause after 2 consecutive
Fakeout / MEV liquidityFill-or-Kill only — never GTC; VWAP pre-check to $100 depth
Oracle settlement delayMax-duration filter: refuse markets resolving > 30 days out
API rate limiting (429)Token-bucket Governor capped at 10 req/s
SlippageVWAP-adjusted spread check; FOK cancels if unfillable
Duplicate firingCanonical-hash dedup with a 30 s cooldown + cache eviction
Cascading failuresCircuit breaker: 3 consecutive failures → auto-pause + alert
WebSocket dropPause + exponential backoff (1→2→4→8→30 s) + full state re-sync
False relationshipDual confidence gate: Brain persists ≥ 0.70, Engine trades ≥ 0.90

All tiers emit structured logstracing (Rust), loguru JSON (Python), pino/winston (TS).


Deployment

ArbOS ships fully containerized via docker-compose.yml (Postgres + Brain + Ingestor + Frontend), with multi-stage, non-root Dockerfiles for the Brain (uv) and Ingestor (rust:1.93-slim). The container-native design maps cleanly onto a managed, highly-available cloud topology:

flowchart TB
DNS["Route 53"] --> CF["CloudFront + S3<br/>(dashboard SPA)"]
DNS --> ALB["Application Load Balancer<br/>(TLS + WebSocket)"]
ALB --> B["ECS Fargate · Brain"]
ALB --> I["ECS Fargate · Ingestor"]
ALB --> BOT["ECS Fargate · Bot"]
B --> RDS[("RDS PostgreSQL<br/>Multi-AZ")]
BOT --> REDIS[("ElastiCache Redis")]
ECR["Amazon ECR"] -. images .-> B
ECR -. images .-> I
ECR -. images .-> BOT
SM["Secrets Manager<br/>API + wallet keys"] -. secrets .-> BOT
classDef aws fill:#232F3E,stroke:#FF9900,color:#F8FAFC;
class DNS,CF,ALB,B,I,BOT,RDS,REDIS,ECR,SM aws;
Loading

See REPORT.md for the full reference architecture (Terraform IaC, Prometheus/Grafana observability, CloudWatch, ECR image scanning).


Documentation

DocumentContents
PROJECT.mdArchitecture & theoretical foundations (the "alpha"), tier specs, launch checklist
REPORT.mdDetailed engineering report + inferred production cloud topology
AGENTS.mdContext & rules for AI-agent contributors
CONTRIBUTING.mdContribution guidelines
CODE_OF_CONDUCT.mdCommunity standards

Contributing

Contributions are welcome! Please read CONTRIBUTING.md and follow the Conventional Commits + scoped-branch workflow. Never push directly to main — open a clean, modular pull request. Ensure cargo fmt, clippy, ruff, and the frontend typecheck all pass before submitting.


License

Released under the MIT License. © 2026 Harikeshav Rameshkumar.


Built for the IBM SkillsBuild Hackathon · Fintech Track · February 2026

Powered by IBM watsonx.ai & Granite

About

Automated prediction market arbitrage engine for Polymarket (Polygon PoS). Arb-OS finds logical arbitrage inside Polymarket, situations where order-book prices violate basic relationships between events, then executes hedged trades for risk-controlled profit after fees and gas.

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages