# MACHINEODDS_AGENT_SKILL_V9.md

**Status:** Customer-facing Agent Skill for autonomous machineODDS trading agents  
**Date:** 2026-06-08  
**Version:** V9.1 — V9 plus real SDK / CLI / MCP access rails  
**Purpose:** Give this Skill to a customer-controlled agent so it can set up paid machineODDS signal access, request the right credentials, trade through external venue adapters, maintain a truthful local ledger, reconcile fills, and run on a repeatable supervised loop.

---

## 0. Read this first

machineODDS provides paid, structured sports prediction-market signal JSON.

machineODDS does **not**:
- custody funds,
- control customer wallets,
- execute venue trades for customers,
- guarantee profit,
- guarantee fills,
- guarantee venue availability,
- replace the customer’s risk policy.

The customer agent is responsible for:
- paid signal access,
- wallet/payment setup,
- Polymarket and/or Kalshi account setup,
- venue credentials,
- trade sizing and risk limits,
- order execution through customer-controlled venue adapters,
- fill verification,
- local ledger,
- reconciliation,
- safe repeat operation.

The correct customer-agent loop is intentionally simple:

```text
paid machineODDS signal
-> deterministic candidate selection
-> venue preflight
-> venue order attempt
-> confirmed fill or position delta
-> append local ledger
-> reconcile venue truth
-> update local/public-safe stats
-> repeat
```

A signal is not a trade.

An accepted order is not always a fill.

Only venue-confirmed fills or position deltas may enter the trading ledger.

---

## 1. The agent’s first job: ask the operator for the right setup

Before writing code or trading, the agent must ask the operator which mode they want.

Minimum questions:

```text
1. Which venues should I trade?
   - Polymarket
   - Kalshi
   - both

2. Which paid access rail should I use?
   - x402 USDC
   - $mODDS credits
   - both

3. What risk caps should I enforce?
   - max stake per trade
   - max daily spend
   - max open positions
   - allowed sports/markets, if any

4. Should I run dry-run first?
   - recommended: yes

5. Should I trade only Premium signals?
   - recommended: yes

6. Should I keep a public-safe proof export?
   - optional
```

The agent must then request only the credentials needed for the selected mode.

---

## 2. Required credentials and environment variables

Use local environment files. Never paste secrets into public logs. Never commit real `.env` files.

### 2.1 machineODDS base

```bash
MACHINEODDS_API_BASE=https://machineodds.live
```

---

## 2A. Optional SDK / CLI / MCP access rails

machineODDS now has real SDK, CLI, and MCP wrappers for the public signal API.

These are access rails only. They do not add custody, order placement, venue execution, wallet control, fill tracking, or PnL truth.

### 2A.1 Python SDK

Install from the project package when published or from the repo during development.

```bash
python3 -m pip install machineodds
```

Basic usage:

```python
from machineodds_sdk import MachineODDSClient

client = MachineODDSClient()

status = client.get_status()
latest = client.get_latest_signals()
preview = client.get_preview_signal()
x402 = client.get_x402_manifest()
```

Paid strongest signal:

```python
signal = client.get_strongest_signal(x_payment_header="...")
```

Rules:
- SDK wraps public HTTP endpoints only.
- SDK does not place trades.
- SDK does not handle private keys.
- SDK does not create or sign x402 payments by itself.
- SDK may pass through an existing x402 payment header produced by approved buyer tooling.

### 2A.2 CLI

CLI command:

```bash
machineodds
```

Useful commands:

```bash
machineodds doctor
machineodds status --json
machineodds latest --json
machineodds preview --json
machineodds x402 --json
machineodds strongest --json
```

Expected behavior:
- `latest`, `preview`, `status`, `inventory`, `capabilities`, and `x402` are read-only.
- unpaid `strongest` returns a clear x402 payment-required response.
- no CLI command executes trades or touches venue credentials.

### 2A.3 MCP server

MCP command:

```bash
machineodds-mcp
```

Safe read-only MCP tools:

```text
get_machineodds_status
get_latest_signals
get_preview_signal
get_strongest_signal
get_capabilities
get_inventory
get_x402_manifest
```

MCP rules:
- expose signal data only,
- do not expose execution,
- do not expose venue auth,
- do not expose wallets,
- do not expose balances,
- do not expose order placement,
- do not invent fills, PnL, or realised win rate.

Customer agents may use SDK, CLI, or MCP to fetch machineODDS signal JSON, but external venue execution remains the customer agent’s separate responsibility.


### 2.2 x402 USDC paid signal access

Use this if the operator wants stateless per-call paid access.

```bash
X402_BUYER_ADDRESS=
X402_BUYER_PRIVATE_KEY=
```

The private key must belong to the wallet that pays x402 invoices.

### 2.3 $mODDS credit access

Use this if the operator wants prepaid reusable credits.

```bash
BASE_RPC_URL=
MODDS_WALLET_ADDRESS=
MODDS_PRIVATE_KEY=
```

`MODDS_WALLET_ADDRESS` means the customer-controlled wallet that holds and sends `$mODDS`. It is **not** the machineODDS receiver wallet.

$mODDS token:

```text
Network: Base
Token: 0xF7B36e3CEf28D77dD6193531C54E6fB34990ABA3
```

### 2.4 Polymarket execution

Use this only if Polymarket is enabled.

```bash
POLYMARKET_PRIVATE_KEY=
POLYMARKET_WALLET_ADDRESS=
POLYMARKET_PROXY_ADDRESS=
POLYMARKET_FUNDER=
POLYMARKET_SIGNATURE_TYPE=3
POLYMARKET_CHAIN_ID=137
POLYMARKET_HOST=https://clob.polymarket.com
```

Rules:
- `POLYMARKET_PRIVATE_KEY` is the signer EOA private key.
- `POLYMARKET_WALLET_ADDRESS` is the signer EOA address.
- `POLYMARKET_PROXY_ADDRESS` is the funded Polymarket proxy/deposit wallet.
- `POLYMARKET_FUNDER` is the funded Polymarket proxy/deposit wallet.
- These must all match the active trading identity.
- A 401 / invalid API key error means stop or skip, not “probably traded.”

### 2.5 Kalshi execution

Use this only if Kalshi is enabled.

```bash
KALSHI_API_KEY_ID=
KALSHI_PRIVATE_KEY_FILE=
KALSHI_ENV=prod
```

Rules:
- Prefer `KALSHI_PRIVATE_KEY_FILE`.
- Use RSA-PSS SHA256 over `timestamp_ms + METHOD + path`.
- Use `PSS.MAX_LENGTH` salt.
- Use headers:
  - `KALSHI-ACCESS-KEY`
  - `KALSHI-ACCESS-TIMESTAMP`
  - `KALSHI-ACCESS-SIGNATURE`
- Use order endpoint:
  - `/portfolio/orders`
- Do not use HMAC.
- Do not use `X-Kalshi-*` headers.

---

## 3. Payment rails

Customer agents may use x402 USDC, `$mODDS` credits, or both.

### 3.1 x402 USDC

Canonical flow:

```text
request paid endpoint
-> receive 402 x402 challenge
-> pay challenge
-> retry request with payment header
-> receive paid signal JSON
```

Paid endpoint:

```text
POST https://machineodds.live/signal/strongest
```

Use x402 when the operator wants simple stateless per-call access.

### 3.2 $mODDS credits

Canonical flow:

```text
check credit balance
-> if balance >= 1, spend existing credit
-> if balance == 0, request quote
-> transfer exact quoted $mODDS on Base
-> verify tx
-> check balance again
-> request signable credit message
-> sign exact returned message
-> spend one credit
-> receive paid signal JSON
```

Endpoints:

```text
GET  /credits/modds/quote?wallet=<wallet_address>
POST /credits/verify-modds
GET  /credits/balance?wallet=<wallet_address>
GET  /signal/credit/message?wallet_address=<wallet>&nonce=<uuid>&expires_at=<unix_seconds>
POST /signal/credit
```

`POST /credits/verify-modds` payload:

```json
{
  "tx_hash": "0x...",
  "wallet_address": "0x..."
}
```

`POST /signal/credit` payload:

```json
{
  "wallet_address": "0x...",
  "nonce": "<uuid>",
  "expires_at": 1780000000,
  "signature": "0x..."
}
```

Success classifier:

```text
payment_verified = true
and signal_id exists
and/or agent_trade_ticket exists
```

Do not require an `ok: true` wrapper. The successful response may be the full paid signal object.

Important:
- Do not buy another credit if `credit_balance >= 1` or `can_spend = true`.
- Do not retry transfers blindly while a tx may still be pending.
- Use exact quoted amount only.
- Use short expiry windows for credit spend messages.
- Never log raw signatures publicly.

---

## 4. Paid signal eligibility

The agent must not trade free preview rows.

Eligible paid signals must satisfy:

```text
api_signal_status == executable_fresh
api_execution_allowed == true
provider_lane == premium
```

Recommended default:

```text
Premium only.
Strongest fallback only if the operator explicitly enables it.
```

The agent must skip any signal with:
- stale freshness,
- missing venue,
- missing required market identifiers,
- missing price/orderbook data,
- resolved/closed market,
- duplicate open exposure,
- unsupported venue,
- venue auth failure.

---

## 5. Candidate selection

The agent must build a ranked queue and iterate through candidates.

Correct behavior:

```python
for candidate in ranked_candidates:
    if not eligible(candidate):
        record_skip(candidate, reason)
        continue

    if duplicate_open_exposure(candidate):
        record_skip(candidate, "duplicate_open_exposure")
        continue

    if not venue_preflight_ok(candidate):
        record_skip(candidate, reason)
        continue

    result = execute(candidate)

    if result.confirmed_fill:
        append_ledger(result)
        reconcile()
        continue

    record_skip(candidate, result.reason)
```

Incorrect behavior:

```python
candidate = ranked_candidates[0]
execute(candidate)
stop()
```

The agent should keep scanning ranked eligible paid signals until:
- max open positions reached,
- max daily spend reached,
- no eligible distinct candidate remains,
- repeated fatal venue/auth/payment fault occurs.

---

## 6. Duplicate and consumed-market guards

The agent must maintain duplicate guards.

Track at least:

```text
open_exposure_keys
cycle_consumed_keys
all_time_consumed_keys
recent_failed_signal_keys
```

Recommended identity keys:

```text
signal_id
venue + market_ticker
venue + condition_id
venue + token_id
venue + market_title + outcome + side
```

Rules:
- Do not buy the same open exposure twice.
- Do not let failed attempts pollute confirmed open exposure.
- Failed attempts may be logged separately.
- Public proof and win rate must use confirmed venue positions only.

---

## 7. Venue adapter contract

Venue execution must be isolated into adapters.

Each adapter must return a structured result:

```json
{
  "venue": "polymarket",
  "signal_id": "pythia_premium_poly_...",
  "trade_attempted": true,
  "order_confirmed": true,
  "confirmed_fill": true,
  "position_delta_confirmed": true,
  "order_id": "0x...",
  "trade_id": null,
  "tx_hash": null,
  "price": 0.47,
  "size_usd": 1.00,
  "blocker": null
}
```

If the adapter cannot prove fill or position delta:

```json
{
  "trade_attempted": true,
  "order_confirmed": false,
  "confirmed_fill": false,
  "position_delta_confirmed": false,
  "blocker": "order_accepted_but_no_fill_or_position_delta"
}
```

Only confirmed fills or position deltas may enter the ledger.

---

## 8. Polymarket adapter rules

Before order placement, require:

```text
token_id present
condition_id present
api_signal_status == executable_fresh
api_execution_allowed == true
market not resolved/closed
CLOB /book?token_id returns usable book
```

Normalize token ids safely:

```python
token_id = str(
    signal.get("token_id")
    or signal.get("outcome_token_id")
    or ""
).strip()
```

Do not call `.strip()` directly on a possible integer.

If CLOB book returns 404 or unusable book:
- skip candidate,
- record `polymarket_book_not_found`,
- continue to next candidate.

If venue auth returns 401:
- fail closed or skip per operator policy,
- record `polymarket_auth_401`,
- do not mark trade opened.

Required Polymarket ledger fields:

```text
signal_id
venue
market_title
condition_id
token_id
held_outcome
opposite_outcome if available
order_id
trade_id if available
tx_hash if available
price
size_usd
submitted_at
matched_at/fill status
provider_lane
rank
reconciliation_status
reconciliation_confidence
```

---

## 9. Kalshi adapter rules

Before order placement, require:

```text
market_ticker present
side/outcome present
api_signal_status == executable_fresh
api_execution_allowed == true
orderbook usable
auth preflight passed
```

Kalshi orderbook note:

When orderbook data exposes `yes_dollars` and `no_dollars`, treat them as resting bid books.

For buying YES:

```text
yes_bid = max(yes_dollars)
no_bid  = max(no_dollars)
yes_ask = round(1 - no_bid, 2)
no_ask  = round(1 - yes_bid, 2)
```

A parser that derives `yes_ask = 0.01` from the lowest `yes_dollars` level is inverted and unsafe.

Kalshi price formatting:

```json
{
  "price": "0.90"
}
```

Use fixed two-decimal string prices for dollar-style prices.

Minimum spend sizing example:

```python
order_count = ceil(min_trade_usd / chosen_limit_price)
spend_amount = order_count * chosen_limit_price

if spend_amount > max_trade_usd:
    reject(candidate, "spend_over_max")
```

Example order body shape:

```json
{
  "ticker": "KX...",
  "client_order_id": "<uuid>",
  "side": "bid",
  "action": "buy",
  "count": "2",
  "price": "0.90",
  "time_in_force": "immediate_or_cancel",
  "self_trade_prevention_type": "taker_at_cross",
  "post_only": false,
  "cancel_order_on_pause": true,
  "exchange_index": 0
}
```

After order submission, record:

```text
status_code
order_id
fill_count
remaining_count
average_fill_price
average_fee_paid
position_before
position_after
confirmed_fill
```

Only confirmed fills or position deltas may enter the ledger.

Required Kalshi ledger fields:

```text
signal_id
venue
market_title
market_ticker
side
outcome
order_id
client_order_id
fill_count
avg_fill_price
fee
size_usd
submitted_at
matched_at/fill status
provider_lane
rank
reconciliation_status
reconciliation_confidence
```

---

## 10. Ledger and reconciliation rules

The local ledger is the customer agent’s trading truth.

The ledger must include only:
- venue-confirmed fills,
- position-delta-confirmed trades,
- venue-reconciled resolved outcomes.

The ledger must not include as successful trades:
- selected signals,
- order requests only,
- failed venue attempts,
- 401 auth failures,
- invalid token attempts,
- stale market attempts,
- no-fill attempts.

Failed attempts should go to a separate debug log.

Recommended structure:

```text
artifacts/
  ledger/
    trades.jsonl
    open_positions.json
    resolved_positions.json
    stats.json
  debug/
    failed_attempts.jsonl
    latest_blockers.json
  runtime/
    latest_cycle_result.json
    heartbeat.json
```

Ledger stats must be derived from customer-linked trades only.

Never compute customer stats from broad wallet history unless every wallet trade belongs to that customer agent. If a venue wallet includes unrelated manual/personal trades, filter by exact agent-linked order ids, client order ids, signal ids, or known agent start time plus agent-created identifiers.

Best rule:

```text
customer_agent_stats = exact agent-linked trades only
not entire wallet history
```

---

## 11. Reconcile after every fill

After every confirmed fill:

```text
write execution result
-> append ledger
-> run venue reconciliation
-> update open/resolved state
-> update stats
-> export public-safe state if enabled
-> continue
```

Do not wait for a later manual audit before updating the ledger.

Reconciliation should check:
- open position exists,
- order/trade id matches,
- market status,
- resolved outcome when available,
- redeem/loss/win state when available.

For unresolved trades:
```text
status = open/unresolved
```

For resolved trades:
```text
status = resolved/correct
or
status = resolved/incorrect
```

Never invent a resolved result.

---

## 12. Supervised loop

Recommended loop:

```python
while True:
    acquire_lock()

    refresh_paid_signal_if_needed()
    candidates = build_ranked_candidates()
    run_execution_until_caps_or_no_candidates()
    reconcile_all_known_positions()
    export_stats()
    write_heartbeat()

    release_lock()
    sleep(cycle_sleep_seconds)
```

The loop should support:

```bash
python3 supervised_loop.py --once
python3 supervised_loop.py --status
python3 supervised_loop.py --stop
python3 supervised_loop.py --max-runtime-seconds 86400
```

Required loop safeguards:
- one process at a time,
- heartbeat file,
- latest cycle result file,
- bounded runtime option,
- stop flag,
- backoff on repeated failures,
- exact blocker fields,
- no duplicate concurrent loops.

A successful cycle is either:

```text
A. confirmed fill path:
confirmed_fill = true
ledger_append_ok = true
reconcile_ok = true
stats_export_ok = true
```

or:

```text
B. no-trade path:
no eligible candidate existed
or all candidates were skipped with exact reasons
```

Anything else is degraded or failed.

---

## 13. Risk policy

Default customer-safe policy:

```json
{
  "allowed_venues": ["polymarket", "kalshi"],
  "allowed_signal_tiers": ["premium"],
  "premium_only": true,
  "default_trade_usd": 1,
  "max_trade_usd": 5,
  "max_daily_spend_usd": 50,
  "max_open_positions": 20,
  "no_duplicate_condition_id_exposure": true,
  "no_duplicate_market_ticker_exposure": true,
  "no_duplicate_token_id_exposure": true,
  "no_duplicate_signal_id_exposure": true,
  "require_executable_fresh": true,
  "require_api_execution_allowed": true,
  "require_confirmed_fill_before_ledger": true
}
```

Do not use Kelly sizing, bankroll-percent sizing, martingale, averaging down, or automatic size escalation unless the operator explicitly asks for it and tests it.

---

## 14. Required artifacts

At minimum, the agent should write:

```text
artifacts/latest_paid_signal.json
artifacts/latest_candidates.json
artifacts/latest_cycle_result.json
artifacts/runtime/heartbeat.json
artifacts/ledger/trades.jsonl
artifacts/ledger/stats.json
artifacts/debug/failed_attempts.jsonl
```

A good `latest_cycle_result.json`:

```json
{
  "cycle_ok": true,
  "generated_at_utc": "2026-06-06T00:00:00Z",
  "paid_signal_source": "modds_credit",
  "paid_signal_valid": true,
  "candidates_seen": 12,
  "candidates_skipped": 8,
  "trade_attempted": true,
  "selected_venue": "polymarket",
  "selected_signal_id": "pythia_premium_poly_...",
  "order_confirmed": true,
  "confirmed_fill": true,
  "ledger_append_ok": true,
  "reconcile_ok": true,
  "stats_export_ok": true,
  "blockers": []
}
```

A failed/no-trade cycle should still be structured:

```json
{
  "cycle_ok": true,
  "trade_attempted": false,
  "confirmed_fill": false,
  "blockers": [
    "no_executable_fresh_signals"
  ]
}
```

---

## 15. Public-safe export

Public-safe export is optional.

If enabled, it must include only safe fields:
- venue,
- market title,
- side/outcome,
- size,
- open/resolved status,
- public-safe order id prefix,
- signal id,
- proof URL if applicable,
- aggregate stats.

Never include:
- private keys,
- API keys,
- raw signatures,
- raw auth headers,
- raw env files,
- full wallet secrets,
- full RPC URLs,
- sensitive venue payloads.

Public-safe proof must not outrun execution.

Allowed:

```text
confirmed fill
-> ledger
-> reconcile
-> public-safe export
```

Not allowed:

```text
signal selected
-> public proof
```

Not allowed:

```text
order request accepted
-> public proof
```

---

## 16. Exact blockers

Agents must produce exact blockers, not vague text.

Use blocker strings like:

```text
stale_paid_batch
no_executable_fresh_signals
missing_signal_id
missing_condition_id
missing_token_id
missing_market_ticker
duplicate_open_exposure
duplicate_consumed_key
polymarket_book_not_found
polymarket_market_resolved
polymarket_auth_401
kalshi_orderbook_parse_failed
kalshi_orderbook_parse_inverted
kalshi_invalid_price_precision
kalshi_auth_failed
order_accepted_but_no_fill_or_position_delta
venue_order_error
modds_credit_balance_zero
modds_verify_insufficient_confirmations
x402_payment_failed
daily_spend_cap_reached
open_position_cap_reached
```

Avoid:
- “probably bought”
- “looks successful”
- “agent decided not to trade”
- “maybe stale”
- “should be fine”

---

## 17. Dry-run and live checklist

Before live trading:

```text
1. Env files exist and secrets are not printed.
2. machineODDS paid signal fetch works.
3. x402 or $mODDS rail works.
4. Paid signal contains executable_fresh row.
5. Candidate queue builds.
6. Duplicate guard works.
7. Polymarket preflight works if enabled.
8. Kalshi preflight works if enabled.
9. One venue canary order succeeds or dry-run proves body shape.
10. Fill/position delta verification works.
11. Ledger append works only after confirmed fill.
12. Reconciliation updates open/resolved state.
13. Stats export reflects ledger only.
14. Supervised loop can run once.
15. Stop/status/heartbeat work.
```

Only after this should the operator enable continuous live mode.

---

## 18. Minimal implementation sequence for an agent

If you are an agent receiving this Skill, build in this order:

```text
1. Create project folder and env templates.
2. Ask operator for selected venues, payment rail, and risk caps.
3. Implement paid signal fetch.
4. Implement $mODDS or x402 payment rail.
5. Implement candidate selector.
6. Implement duplicate/exposure guard.
7. Implement Polymarket adapter if enabled.
8. Implement Kalshi adapter if enabled.
9. Implement confirmed-fill classifier.
10. Implement ledger append.
11. Implement reconciliation.
12. Implement stats export.
13. Implement supervised loop.
14. Run dry-run.
15. Run one live canary.
16. Only then enable continuous loop.
```

Do not start with UI, dashboards, social posting, or complex analytics.

The first production goal is simply:

```text
paid signal
-> trade
-> confirmed fill
-> ledger
-> reconcile
-> repeat
```

---

## 19. Customer-facing status block

```text
Autonomous customer-agent trading: SUPPORTED THROUGH CUSTOMER-RUN AGENTS

Customers can configure their own agents to:
- buy machineODDS paid signals through x402 or $mODDS credits
- select Premium executable-fresh signals
- trade through customer-controlled Polymarket and/or Kalshi adapters
- enforce explicit risk caps
- avoid duplicate exposure
- verify confirmed fills before ledgering
- reconcile open/resolved state
- maintain a truthful local trade ledger
- run under a supervised loop

machineODDS provides paid signal infrastructure and agent-readable JSON.
Customers control wallets, venues, risk settings, execution, and legal/compliance obligations.
```

---

## 20. What not to do

Do not build the customer agent as a chatty free-form reasoning bot.

Do not allow it to:
- trade free preview signals,
- mark an order opened from HTTP 200 alone,
- skip reconciliation,
- count unrelated wallet history,
- compute win rate from unlinked venue history,
- repeat-buy the same open exposure,
- continue after auth failures without exact blocker logging,
- leak secrets in logs/proof files,
- invent fills, PnL, order status, or resolved outcomes.

The customer agent should be boring, deterministic, auditable, and strict.

---

## 21. Final operating invariant

Use this as the final test of correctness:

```text
If a human asks, “Why does your ledger say this trade happened?”
the agent must be able to point to:
1. paid machineODDS signal id,
2. venue order/trade identifier,
3. confirmed fill or position delta,
4. local ledger row,
5. reconciliation state,
6. current open/resolved status.
```

If any of those are missing, the trade should not be counted as an opened trade.

