# POLYODDS_SKILL.md

PolyOdds — Sports Signal Intelligence for Autonomous Agents  
Skill version: 2.2  
Platform: https://polyodds.bet

---

## 1. Purpose

PolyOdds installs sports trading signal intelligence into an autonomous agent.

The agent uses PolyOdds to:

- Discover ranked sports-market signals derived from bookmaker-consensus fair values vs prediction-market prices.
- Inspect transparent strategy templates (policy catalog) to understand which signals meet a chosen risk profile.
- Retrieve structured signal intents — machine-readable advisory payloads with venue, selection, edge, liquidity, and trade-guidance fields.
- Apply strategy/policy verdicts — deterministic, observable verdicts (allow / caution / block / review) grounded in the same signal fields used for ranking.
- Prepare venue trade instructions based on the signal intent for external execution.
- Report external venue fills back to PolyOdds so they are recorded durably in the attribution and performance ledger.
- Review performance from reported executions only — the ledger is the only source of truth for fill data, outcome, and PnL.

PolyOdds provides signal intelligence, not brokered execution. The platform never places orders on behalf of users. Execution happens externally on operator-approved venue accounts (Polymarket, Kalshi) using operator-provided credentials, wallets, and connectors.

---

## 2. Tool Priority

Always prefer the interface that is already wired into the agent runtime:

1. **MCP first** — use MCP tools when the runtime supports them (Claude Desktop, Claude Code, any MCP host).
2. **SDK second** — use the Python SDK (`polyodds` package) for scripted agents and automation.
3. **CLI third** — use `polyodds` CLI for shell-driven or human-in-the-loop operator workflows.
4. **REST fallback** — use direct HTTP calls when none of the above is available.

---

## 3. Authentication and Access

### API key

All authenticated calls require a `pip_...` API key issued via your PolyOdds account.

Set it in the environment before any call:

```bash
export POLYODDS_API_KEY=pip_yourkeyhere
```

Pass it as a Bearer token on REST calls:

```
Authorization: Bearer pip_yourkeyhere
```

### x402 pay-per-call (no account required)

One signal snapshot endpoint accepts micropayment via x402 on Base mainnet instead of an API key:

```
GET https://polyodds.bet/api/x402/resources/top-signals
```

On first call it returns HTTP 402 with a `PAYMENT-REQUIRED` header and a `pay_to` address. Send the required USDC amount on Base, then retry the same request with the header:

```
X-Payment-Tx-Hash: <tx_hash>
```

### Security rules

- Never paste API keys or private keys into chat or logs.
- Store secrets in secure environment variables or the agent runtime's secret store.
- Never log API keys, bearer tokens, or private keys.
- Never use admin routes or private terminal routes. Only use customer-safe surfaces documented in this skill.

---

## 4. Agent Installation / Setup Responsibilities

Before the agent attempts any signal fetch, policy evaluation, or trade, it must complete the following setup in order.

### Step 1 — Install PolyOdds access

Choose the interface that matches the runtime:

1. **MCP connector** — if the runtime supports MCP (Claude Desktop, Claude Code, agentic.market, Hermes-style runtimes), install the `polyodds` MCP server and verify `tools/list` returns the expected tools.
2. **Python SDK / CLI** — install the `polyodds` Python package and verify `polyodds doctor` passes.
3. **REST fallback** — use direct HTTP if neither of the above is available.

Set the API key in the agent's secure environment:

```bash
export POLYODDS_API_KEY=pip_yourkeyhere
```

### Step 2 — Install and validate venue connector(s)

PolyOdds provides signal intelligence. Execution requires a separate venue connector installed in the agent runtime that the operator has explicitly approved. See **Venue Connector Readiness** below.

The agent must not attempt to trade until at least one venue connector is installed, validated, and has confirmed account access and a known balance.

### Step 3 — Load operator risk policy

The operator must supply a bankroll and risk policy before the agent begins trading. See **Operator Risk Policy Template** below. The agent must read and validate this policy before any trade decision.

### Step 4 — Run readiness checks

Before each trade cycle the agent must confirm:

- PolyOdds API key is valid (`polyodds doctor` passes or `capabilities()` returns `ok: true`).
- Venue connector is reachable and returns a known balance.
- Risk policy is present and complete.
- No daily loss or open exposure limit has already been reached.

The agent must not trade if any readiness check fails.

---

## 5. Venue Connector Readiness

PolyOdds provides signal intelligence, not brokered execution. Each venue the agent may trade on requires a separate connector with operator-approved credentials installed in the agent runtime. Wallet approval alone is not sufficient — the connector must be installed, credentials validated, and the venue reachable before the agent may submit orders.

### Polymarket connector readiness

The agent/runtime must verify all of the following before submitting any Polymarket order:

- Polymarket connector library or integration is installed and importable in the agent runtime.
- Approved wallet signing credentials are configured in secure environment variables (never hardcoded). Example variable names used by common connectors (these are examples, not requirements — check your connector's documentation):
  - `POLYMARKET_PRIVATE_KEY`
  - `POLYMARKET_FUNDER_ADDRESS`
  - `POLYMARKET_API_KEY`
  - `POLYMARKET_API_SECRET`
  - `POLYMARKET_API_PASSPHRASE`
- The funder/wallet address is known and matches the operator-approved account.
- CLOB/orderbook access is confirmed — a test market lookup returns a valid response.
- Venue account balance is known and above the minimum required for the intended trade size.
- Limit order submission endpoint is reachable (only required in `approval_required` or `autonomous` mode; not required in `read_only` mode).
- Order status and fill confirmation can be read from the venue connector response.
- No private keys are ever logged, returned in API responses, or included in chat.

### Kalshi connector readiness

The agent/runtime must verify all of the following before submitting any Kalshi order:

- Kalshi connector library or integration is installed and importable in the agent runtime.
- API key ID and private key are configured in secure environment variables. Example variable names (examples only — check your connector's documentation):
  - `KALSHI_API_KEY_ID`
  - `KALSHI_PRIVATE_KEY`
  - `KALSHI_ENV` (e.g. `production` or `demo`)
- `KALSHI_ENV` is set correctly for the intended environment — never use a demo key in production or vice versa.
- Account/balance can be read successfully via the connector.
- Market and orderbook lookup returns a valid response for the target ticker.
- Limit order submission endpoint is reachable (only in `approval_required` or `autonomous` mode).
- Order status and fill confirmation can be read from the connector response.
- No private keys are ever logged, returned in responses, or included in chat.

> **Note:** Kalshi signal availability may depend on platform rollout, plan access, and current venue coverage. Agents must handle empty Kalshi results gracefully. Polymarket is the primary supported signal venue. Agents must still handle empty results, stale markets, or no current executable signals gracefully.

---

## 6. Trading Readiness Checklist

Run this checklist before every trade cycle. The agent must not submit an order unless every applicable item passes.

- [ ] PolyOdds API key is valid and `polyodds doctor` (or `capabilities()`) returns success.
- [ ] Selected `strategy_id` is one of `safer`, `balanced`, or `aggressive` (verified via `get_strategies`).
- [ ] Signal intent is available for the selected `signal_id` (successful `get_signal_intent` call).
- [ ] `policy_verdict` from `apply_signal_policy` is not `block`.
- [ ] `execution_mode` is explicitly set (`read_only`, `approval_required`, or `autonomous`).
- [ ] Operator risk policy is present and complete (bankroll, max trade, daily loss limit).
- [ ] At least one allowed venue is configured and its connector is validated.
- [ ] Venue account balance is known and sufficient for the intended position size.
- [ ] The target market is confirmed open on the venue.
- [ ] Live venue price / orderbook data is fresh (not stale from a prior cycle).
- [ ] Liquidity and slippage checks pass against `min_liquidity_usd` and `max_slippage_pct` in the risk policy.
- [ ] If `execution_mode = approval_required`: explicit operator approval has been received for this trade.
- [ ] If `execution_mode = autonomous`: all policy and readiness checks above have passed; no limit has been reached.

---

## 7. Install-to-Trade Flow

Use this flow as the canonical agent setup and trade sequence:

```
1.  Configure PolyOdds access
      Set POLYODDS_API_KEY. Install MCP / SDK / CLI per runtime capability.
      Verify: polyodds doctor passes or capabilities() returns ok: true.

2.  Configure venue connector(s)
      Install and validate the Polymarket and/or Kalshi connector.
      Set approved credentials in secure env vars.
      Verify: account readable, balance known, market lookup works.

3.  Load operator risk policy
      Read execution_mode, strategy_id, bankroll limits, venue allow-list.
      Validate all required fields are present.

4.  Run readiness checks
      Confirm all items in the Trading Readiness Checklist pass.
      Do not proceed if any check fails.

5.  Fetch strongest signals
      get_strongest_signals() → ranked signals with edge, tier, liquidity.

6.  Get signal intent (Pro+)
      get_signal_intent(signal_id) → venue, selection, edge, trade_guidance.

7.  Apply strategy policy (Pro+)
      apply_signal_policy(signal_id, strategy_id) → policy_verdict.
      Stop if policy_verdict = "block".

8.  Prepare trade ticket
      venue, market_id / ticker, selection, side, size, limit price.
      Apply position sizing from the selected strategy, max_trade_usd, bankroll limits, exposure limits, liquidity, and slippage policy.

9.  read_only mode → explain the signal and ticket only. Do not submit.

10. approval_required mode → present ticket to operator. Wait for approval.
      Do not submit until approval is explicitly received.

11. autonomous mode → submit only if all policy and readiness checks pass.
      "Instant trading" means the agent can begin once setup and readiness
      checks pass. It does not mean bypassing venue credential setup, wallet
      approval, balance checks, price checks, liquidity checks, or operator
      risk limits.

12. Confirm venue order/fill
      Read order status and fill confirmation from the venue connector.
      Never assume a fill occurred without connector confirmation.

13. Call report_execution
      Report the confirmed fill to PolyOdds immediately.
      Include signal_id, venue, fill_price, fill_size or notional_usd,
      outcome, side, and any available signal context fields.

14. Review get_reported_executions
      Periodically call get_reported_executions() to read the performance
      ledger. Compute win rate and PnL from resolved rows only.
```

---

## 8. Execution Modes

Configure one mode before the agent begins acting:

| Mode | Behaviour |
|---|---|
| `read_only` | Agent fetches signals and explains them. No trade preparation, no order placement. |
| `approval_required` | Agent prepares a trade ticket and halts. Execution requires explicit operator approval. |
| `autonomous` | Agent executes without per-trade approval via external venue credentials/connectors. |

**Autonomous mode pre-conditions** (all must be true before the agent may act without approval):

- The operator has explicitly set `execution_mode = autonomous`.
- Operator-approved external venue credentials or connectors are configured and verified.
- A risk policy with bankroll and loss limits is present (see Section 14).
- The agent has confirmed it can reach the venue and the venue balance is known.

PolyOdds provides signal intelligence, policy verdicts, and fill-reporting surfaces. It does not guarantee native venue order placement unless a separate venue connector exists in the operator agent runtime.

---

## 9. Strategy Policy Layer

PolyOdds ships three named, transparent strategy templates. All thresholds are declared explicitly — the agent can inspect, reproduce, or override them independently.

### Exact strategy IDs (use these exact strings in all API/SDK/CLI calls)

| ID | Profile | Description |
|---|---|---|
| `safer` | Conservative-style | High-bar filter. Strong-tier signals only, edge ≥ 8 pp, ≥ 2 h to kickoff, ML support required. Max 2% of bankroll per position. |
| `balanced` | Balanced | Standard filter. Moderate-tier signals, edge ≥ 3 pp, ≥ 1 h to kickoff, ML support preferred but not required. Max 5% of bankroll per position. |
| `aggressive` | High-coverage | Low-bar filter. Any signal with positive edge (≥ 0.5 pp), no time gate. Max 2% of bankroll per position to compensate for lower quality. |

### How the policy verdict is derived

The policy evaluates these signal fields in order:

1. `hours_to_kickoff` — missing → `review`
2. `signal_strength_tier` vs `min_signal_strength` — below required tier → `block`
3. `effective_edge_pct` (COALESCE exec_edge_pct, gamma_edge_pct) vs `min_edge_pct` — below threshold → `block`
4. `hours_to_kickoff` vs `min_hours_to_kickoff` (when strategy has a time gate) → `block`
5. `ml_band` (when `require_ml_support = true`) — not `supported` or `borderline` → `caution`
6. All gates passed → `allow`

Verdicts: `allow` | `caution` | `block` | `review`

### Required agent behaviour

- Always call `get_strategies` first to read the live catalog. Do not invent thresholds or IDs.
- Then call `apply_signal_policy(signal_id, strategy_id)` with the chosen signal and strategy.
- Never rename `safer` to `conservative` in API calls or code. In prose you may describe it as "conservative-style."
- Never invent strategy IDs. Only `safer`, `balanced`, and `aggressive` are valid.

---

## 10. Signal Workflow

```
1. get_strongest_signals()               → ranked signals, liquidity, trade_guidance
2. Select a signal_id from the response
3. get_signal_intent(signal_id)          → full SignalIntent contract (Pro+ required)
4. apply_signal_policy(signal_id, strategy_id)  → PolicyVerdict (Pro+ required)
5. Inspect from the intent response:
     venue, event_title, market_type, selection/outcome,
     effective_edge_pct, hours_to_kickoff, signal_strength_tier,
     liquidity fields (best_bid, best_ask, spread, depth_yes, depth_no),
     trade_guidance (if present)
6. If policy_verdict is "block" → do not proceed with this signal.
   If policy_verdict is "caution" → reduce position size or seek approval.
   If policy_verdict is "allow" or "caution" and execution is approved → prepare trade ticket.
7. Prepare the trade ticket:
     venue, market_id or ticker, selection, side, price, size
8. In approval_required mode → present ticket and wait for approval.
   In autonomous mode (with operator connector) → execute via venue connector.
```

All signals where `execution_mode = "external"` are advisory only. The agent or operator executes externally.

---

## 11. External Execution Workflow

- Polymarket and Kalshi execution happens through operator-approved venue accounts, wallets, and connectors provided by the operator's agent runtime. PolyOdds does not hold or move funds.
- In autonomous mode the agent may trade only through those external credentials/connectors.
- The agent must never claim an order was placed unless the venue connector returns confirmed order or fill data.
- After a confirmed fill, the agent must call `report_execution` immediately.

---

## 12. Report Execution / Performance Ledger

Execution truth in PolyOdds exists **only** when explicitly reported by the agent or operator after an external venue fill. PolyOdds never infers fills from wallet history or click-through data.

Call `report_execution` after every confirmed external fill. Include as many of these fields as are available:

| Field | Required | Notes |
|---|---|---|
| `venue` | ✅ | `polymarket` \| `kalshi` \| `other` |
| `fill_price` | ✅ | 0.0–1.0 for binary markets |
| `fill_size` or `notional_usd` | ✅ (one of) | Shares or USD notional |
| `signal_id` or `match_title` | ✅ (one of) | Links report to the signal |
| `outcome` | recommended | Selection name (e.g. "YES", "Team A") |
| `side` | recommended | "BUY" or "SELL" |
| `signal_price` | recommended | Venue price at signal time |
| `signal_edge_pct` | recommended | Edge at signal time (exec_edge or gamma_edge) |
| `poly_market_id` | recommended | Polymarket market slug or condition ID |
| `kalshi_ticker` | recommended | Kalshi REST ticker (when venue=kalshi) |
| `condition_id` | recommended | Gamma conditionId |
| `sport_key` | optional | e.g. `basketball_nba` |
| `market_type` | optional | e.g. `moneyline`, `totals`, `spread` |
| `fee_usd` | optional | Venue fee paid |
| `external_order_id` | optional | Venue order ID |
| `external_trade_id` | optional | Venue trade/fill ID |
| `notes` | optional | Free-text notes |

Call `get_reported_executions` to read back the ledger and verify entries were recorded.

The PolyOdds reported execution ledger stores each fill you report. Resolution fields written by the platform after markets settle:

- `resolved_price` — market settlement price (1.0 = win, 0.0 = loss for binary)
- `pnl_usd` — realised PnL in USD
- `resolved_at` — timestamp when resolution was written

---

## 13. Performance Tracking Rules

The agent may calculate the following **only from `get_reported_executions` data**:

- Total reported trades (all rows)
- Resolved trades (rows where `resolved_at` is not null and `status = "resolved"`)
- Open / unresolved trades (rows where `resolved_at` is null) — exclude from all win-rate and PnL calculations
- Win / loss counts — from `pnl_usd > 0` / `pnl_usd < 0` on resolved rows only, or from `resolved_price`
- Win rate — from resolved rows only (wins ÷ resolved count)
- Realised PnL — sum of `pnl_usd` on resolved rows only
- Breakdowns by venue, sport_key, market_type, or strategy if those fields are present in the reported execution rows

Do not:

- Invent ROI, PnL, or win rate from chat memory or agent inference.
- Report win rate or PnL from unresolved open trades.
- Mix data from any source other than the reported execution ledger for performance claims.
- Invent Brier Score or calibration metrics; those are platform-derived, not agent-computed.

If resolution data is missing or partial, state explicitly what is missing rather than estimating.

---

## 14. Operator Risk Policy Template

This policy is stored in the agent runtime and enforced by the agent and its operator. PolyOdds does not currently store this policy on the platform side.

```yaml
risk_policy:
  execution_mode: read_only           # read_only | approval_required | autonomous
  strategy_id: balanced               # safer | balanced | aggressive
  bankroll:
    total_usd: 500                    # total capital available
    currency: usd
  max_trade_usd: 10                   # maximum notional per trade
  max_daily_loss_usd: 50              # daily loss limit; pause if reached
  max_open_exposure_usd: 100          # total outstanding positions limit
  allowed_venues:
    - polymarket
    - kalshi
  require_limit_orders: true          # always use limit orders (not market)
  max_slippage_pct: 0.5               # reject if fill deviates > 0.5% from signal price
  min_liquidity_usd: 50               # reject if estimated executable depth < $50
  require_approval_above_usd: 25      # always seek approval for trades > $25
  stop_after_daily_loss_usd: 50       # halt all activity after this daily loss
  pause_if_consecutive_losses: 3      # pause after 3 consecutive losing reported trades
  duplicate_trade_guard:
    enabled: true
    cooldown_minutes_per_signal: 60   # minimum minutes before the same signal_id may be re-entered
    max_entries_per_signal: 1         # hard cap on entries for a given signal_id
    max_entries_per_market_outcome_side: 1  # hard cap per market+outcome+side tuple
    allow_scale_in: false             # if true, operator explicitly permits adding to existing position
    require_approval_for_scale_in: true     # require explicit approval even when allow_scale_in=true
    block_if_existing_open_order_unknown: true  # halt if position/order state cannot be determined
```

Adjust values to the operator's capital and risk tolerance. The agent must read and enforce this policy on every trade decision cycle.

---

## 15. Autonomous Safety Rules

The agent must never trade if any of the following conditions is true:

- `execution_mode` is not `autonomous`
- Operator has not supplied external venue credentials or connectors
- Risk policy is missing or incomplete
- Bankroll limits are missing or unknown
- Venue account balance is unknown (where required for sizing)
- Market price has moved beyond `max_slippage_pct` from the signal price
- Estimated liquidity is below `min_liquidity_usd`
- Signal age indicates it is stale (outside the active ranking window)
- `policy_verdict` is `block`
- Daily loss limit (`stop_after_daily_loss_usd`) has been reached
- Open exposure limit (`max_open_exposure_usd`) has been reached
- Consecutive loss pause threshold (`pause_if_consecutive_losses`) has been reached

When any condition blocks execution, the agent must log the reason and halt that trade. It must not find workarounds or reframe the conditions to proceed.

---

## 16. Duplicate Trade / Repeat Entry Guardrails

**Ranked visibility does not mean permission to re-enter.**

A signal remaining visible in `get_strongest_signals` or continuing to rank highly is not a new entry signal. The agent must treat a signal it has already acted on as acted-on until a new policy-allowed decision is made.

### What counts as the "same trade"

The agent must treat two trade actions as the same trade when they match substantially on any of the following fields:

- `signal_id` (when available — highest confidence)
- `venue`
- `market_id`, `poly_market_id`, `condition_id`, or `kalshi_ticker`
- `market_type`
- `outcome` / selection
- `side`
- event title / `match_title` (when market IDs are unavailable)

### Pre-trade duplicate check

Before preparing or submitting any trade, the agent must check all three sources in order:

1. **Local runtime state** — has this signal_id or market+outcome+side been acted on in the current trade cycle?
2. **Venue connector state** — does an open order or existing position already exist for this market+outcome+side?
3. **PolyOdds reported execution ledger** (`get_reported_executions`) — has a fill already been reported for this signal_id or market+outcome+side?

If any check returns a positive match, the agent must apply the `duplicate_trade_guard` policy and skip or block the trade.

### Conditions that block a repeat entry

The agent must not submit another order for the same trade if any of the following is true:

- An open order already exists for the same market/outcome/side (from venue connector state).
- A fill has already been reported for the same `signal_id` (from the PolyOdds execution ledger).
- The same trade was submitted during the current trade cycle (from local runtime state).
- The same signal was acted on within the configured `cooldown_minutes_per_signal` window.
- Existing exposure to that market would exceed `max_trade_usd` or `max_open_exposure_usd`.
- The agent cannot determine whether the position or open order already exists (`block_if_existing_open_order_unknown: true`).

### Structured duplicate skip reasons

When a duplicate check blocks a trade, the agent must log one of these structured reasons:

| Reason | Condition |
|---|---|
| `duplicate_signal_already_traded` | `signal_id` already has a reported fill in the execution ledger |
| `duplicate_market_outcome_side` | Same market+outcome+side already has a reported fill (signal_id unavailable) |
| `existing_open_order_found` | Venue connector reports an open order for this market+outcome+side |
| `existing_position_found` | Venue connector reports an existing position for this market+outcome+side |
| `cooldown_active` | Time since last action on this signal_id is less than `cooldown_minutes_per_signal` |
| `scale_in_not_allowed` | `allow_scale_in: false` and a position already exists |
| `existing_exposure_limit_reached` | Adding this trade would exceed `max_open_exposure_usd` |
| `position_state_unknown` | Venue connector or ledger state is unavailable and `block_if_existing_open_order_unknown: true` |

### Scale-in rules

If `allow_scale_in: true`, the agent may add to an existing position only when the operator policy explicitly permits it. A scale-in is not a bypass of duplicate checks — all of the following must still be re-run and pass:

- Signal freshness (is the signal still within the active ranking window?)
- Live venue price (is the current ask still within `max_slippage_pct`?)
- Liquidity (is depth still above `min_liquidity_usd`?)
- Slippage (would the fill price deviate beyond policy?)
- Bankroll limit (does the existing position plus this addition exceed `max_trade_usd`?)
- Open exposure limit (does total exposure remain within `max_open_exposure_usd`?)
- Daily loss limit (has `stop_after_daily_loss_usd` been reached?)
- Existing position size (what is the current open notional for this market?)
- Strategy policy verdict (does `apply_signal_policy` still return `allow` or `caution`?)

Scale-ins require operator approval by default (`require_approval_for_scale_in: true`). The agent must not perform a scale-in autonomously unless `require_approval_for_scale_in: false` is explicitly set and all checks above pass.

---

## 17. Approval Mode

In `approval_required` mode the agent:

1. Runs the full signal workflow through policy verdict.
2. Prepares a trade ticket with venue, market, selection, side, size, price, and policy verdict.
3. Presents the ticket to the operator and halts.
4. Does not execute until the operator provides explicit approval.
5. After approval, executes via the external venue connector, then calls `report_execution`.

---

## 18. Read-Only Mode

In `read_only` mode the agent:

1. Fetches signals and explains their edge, strength tier, venue, and intent.
2. May apply policy verdicts for advisory output.
3. Does not prepare executable order instructions.
4. Does not place trades.
5. Does not call `report_execution` (no fills to report).

---

## 19. Example Operator Prompts

```
Set up PolyOdds in read-only mode.
```
→ Set `execution_mode: read_only`. Fetch strongest signals and explain them. Do not prepare trade tickets.

```
Use balanced strategy and ask approval for every trade.
```
→ Set `execution_mode: approval_required`, `strategy_id: balanced`. Apply policy verdicts and present trade tickets awaiting approval.

```
Use safer strategy with $500 bankroll and max $10 per trade.
```
→ Set `strategy_id: safer`, `bankroll.total_usd: 500`, `max_trade_usd: 10`. Block any signal where `policy_verdict != allow` (safer requires strong tier, edge ≥ 8 pp, ML support).

```
Use autonomous mode with external Polymarket connector and report all fills.
```
→ Set `execution_mode: autonomous`, `allowed_venues: [polymarket]`. Confirm connector is configured and bankroll/risk policy is present. Call `report_execution` after every fill.

```
Show my reported executions.
```
→ Call `get_reported_executions()`. Display the ledger.

```
What is my resolved win rate and PnL?
```
→ Call `get_reported_executions()`. Filter to rows where `resolved_at` is not null and `status = "resolved"`. Compute win rate from those rows only. Sum `pnl_usd` for realised PnL. Exclude open trades from both calculations.

---

## 20. Exact Command and API Examples

### CLI

```bash
# Verify auth and data health
polyodds doctor

# Fetch strongest signals (human-readable)
polyodds signals strongest

# Fetch strongest signals (machine-readable JSON)
polyodds signals strongest --json

# Retrieve the SignalIntent contract for a specific signal (Pro+)
polyodds signals intent <signal_id> --json

# Report an external fill
polyodds report-execution \
  --venue polymarket \
  --fill-price 0.65 \
  --fill-size 100 \
  --signal-id <signal_id> \
  --outcome "YES" \
  --side BUY

# List reported executions
polyodds executions list --json

# Delete a reported execution (soft-delete)
polyodds executions delete <execution_id>
```

### MCP (stdio — Claude Desktop / Claude Code)

```bash
pip install polyodds
export POLYODDS_API_KEY=pip_yourkeyhere
polyodds mcp serve
```

Claude Desktop config:

```json
{
  "mcpServers": {
    "polyodds": {
      "command": "polyodds",
      "args": ["mcp", "serve"],
      "env": { "POLYODDS_API_KEY": "pip_yourkeyhere" }
    }
  }
}
```

MCP tool calls (remote HTTP, curl):

```bash
# List available tools
curl -s -X POST https://polyodds.bet/mcp \
  -H "Authorization: Bearer pip_yourkeyhere" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

# get_strategies — no plan gate
curl -s -X POST https://polyodds.bet/mcp \
  -H "Authorization: Bearer pip_yourkeyhere" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_strategies","arguments":{}}}'

# apply_signal_policy — Pro+
curl -s -X POST https://polyodds.bet/mcp \
  -H "Authorization: Bearer pip_yourkeyhere" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"apply_signal_policy","arguments":{"signal_id":"<uuid>","strategy_id":"balanced"}}}'

# report_execution
curl -s -X POST https://polyodds.bet/mcp \
  -H "Authorization: Bearer pip_yourkeyhere" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"report_execution","arguments":{"venue":"polymarket","fill_price":0.65,"fill_size":100,"signal_id":"<uuid>","outcome":"YES","side":"BUY"}}}'

# get_reported_executions
curl -s -X POST https://polyodds.bet/mcp \
  -H "Authorization: Bearer pip_yourkeyhere" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"get_reported_executions","arguments":{}}}'
```

### Python SDK

```python
from polyodds import PolyOddsClient

client = PolyOddsClient(api_key="pip_yourkeyhere")

# Discover capabilities (no auth required)
caps = client.capabilities()

# Fetch strongest signals
signals = client.get_strongest_signals(limit=10)

# Get signal intent (Pro+)
intent = client.get_signal_intent(signal_id="<uuid>")

# Discover strategy templates
strategies = client.get_strategies()

# Apply strategy policy (Pro+)
verdict = client.apply_signal_policy(signal_id="<uuid>", strategy_id="balanced")

# Report a fill
result = client.report_execution(
    venue="polymarket",
    fill_price=0.65,
    fill_size=100,
    signal_id="<uuid>",
    outcome="YES",
    side="BUY",
    signal_price=0.62,
    signal_edge_pct=5.4,
    external_order_id="order-abc123",
)

# Read reported executions
ledger = client.get_reported_executions(limit=50)
```

### REST API

```bash
# Strategy catalog — no plan gate, standard Bearer auth
GET https://polyodds.bet/v1/agent/strategies
Authorization: Bearer pip_yourkeyhere

# Apply strategy policy — Pro+
POST https://polyodds.bet/v1/agent/signals/<signal_id>/policy
Authorization: Bearer pip_yourkeyhere
Content-Type: application/json
{"strategy_id": "balanced"}

# Strongest signals (primary signal surface)
GET https://polyodds.bet/api/polybot/signals/strongest
Authorization: Bearer pip_yourkeyhere

# Signal intent — Pro+
GET https://polyodds.bet/v1/agent/signals/<signal_id>/intent
Authorization: Bearer pip_yourkeyhere

# Report a fill
POST https://polyodds.bet/api/customer/reported-executions
Authorization: Bearer pip_yourkeyhere
Content-Type: application/json
{
  "venue": "polymarket",
  "fill_price": 0.65,
  "fill_size": 100,
  "signal_id": "<uuid>",
  "outcome": "YES",
  "side": "BUY"
}

# Read reported executions
GET https://polyodds.bet/api/customer/reported-executions
Authorization: Bearer pip_yourkeyhere
```

---

## 21. What the Agent Must Never Do

- **Never invent fills.** A fill exists only when the venue connector confirms it and `report_execution` has been called.
- **Never invent venue balances.** Query the venue connector directly; do not estimate.
- **Never invent order status.** Order status comes only from the venue connector response.
- **Never invent PnL.** PnL comes only from resolved entries in the PolyOdds reported execution ledger via `get_reported_executions`.
- **Never invent Brier Score or calibration metrics.** These are platform-derived; never compute or assert them from memory.
- **Never treat a PolyOdds signal as a guaranteed winner.** Signals are probabilistic advisory intelligence, not certainties.
- **Never bypass the operator risk policy.** If a rule blocks a trade, the agent must not reframe or work around it.
- **Never expose or log API keys, bearer tokens, or private keys.**
- **Never use admin routes, private terminal routes, or internal platform endpoints.**
- **Never mix internal house execution truth with customer reported execution truth.** The only performance truth available to the agent is the reported executions returned by `get_reported_executions`, written via `report_execution`.
- **Never submit the same trade repeatedly because the same signal remains visible, ranked, or returned by `get_strongest_signals`.** Continued ranking is not a new trade signal.
- **Never place a second order for the same market/outcome/side unless the operator policy explicitly allows scale-ins (`allow_scale_in: true`) and all exposure checks pass.**
- **Never assume there is no existing position or open order.** Check venue connector state and the PolyOdds reported execution ledger before every trade action.
- **Never loop over the same signal and keep submitting repeat orders without a new explicit policy-allowed scale-in decision.**
