nansen-copytrader-overlay · v0.1.0 · 2026-08-12 · sha256 c7428d14f2abd608

nansen-copytrader-overlay v0.1.0A

Immutable. This exact content is served forever at /api/v1/blob/c7428d14f2abd608.

---
name: nansen-copytrader-overlay
description: Rank Polymarket copytrading leaders using Nansen as a data layer — pnl-by-market for the target market is the primary signal, address-summary/pnl-by-address is a secondary, credit-guarded refinement. Includes an experimental (dry-run only) live insider scan. Use when Simmer needs to re-rank or filter a leader list before executing copytrading rebalances on a specific Polymarket market.
tags:
  - copytrading
  - polymarket
  - nansen
metadata:
  author: Simmer (@simmer_markets)
  version: "0.1.0"
  displayName: Nansen Copytrader Overlay
  difficulty: intermediate
  simmer:
    credit:
      name: "Alyna Takahashi"
      url: "https://github.com/alyna123t"
      label: by
---

# Nansen Copytrader Overlay

Nansen is a **data layer only** in this skill: PM PnL quality overlay +
address-summary/wallet quality. It is **not** smart-money-labeled
copytrading — Nansen's smart-money labels are not Polymarket-specific, and
no code path here calls the labels endpoint at all.

## When to use this skill

Simmer is about to execute a copytrading rebalance into a specific
Polymarket market and has a candidate leader list (wallets + an existing
`wallet_score`). Use this skill to re-rank that list using Nansen's
Polymarket-specific PnL data before Simmer acts on it.

Do **not** use this skill to:
- Generate smart-money labels or entity classifications (out of scope — see framing above).
- Place trades. Every function and CLI command here is signal-only; the
  caller (Simmer's execution wrapper) owns the actual `--live` gate.

## Core signal: pnl-by-market first

The primary ranking signal is `pnl_by_market(market_id)` — who is actually
profitable **in the target market**, not a wallet's broad trading history.
`pnl_by_address` / `trades_by_address` (broad history) and
`address_summary` (cheap wallet-quality pre-filter) are secondary —
they refine the market-specific score, they never replace it. A wallet
absent from the target market's leaderboard gets tagged
`NOT_IN_MARKET_LEADERBOARD` and keeps its original score with a small
discount, rather than a fabricated 50/50 blend against a signal that
doesn't exist.

## Running it

```bash
python3 -m venv .venv && source .venv/bin/activate
pip install pytest   # for tests only; the skill itself is stdlib-only
export NANSEN_API_KEY=...   # your own Nansen key — sent as the `apiKey` header
```

No `nansen` CLI required: calls go straight to `https://api.nansen.ai/api/v1`
over HTTPS. Bring-your-own-key — every call spends **your** Nansen credits,
so `--max-wallets` (default 30) and `--max-calls` (default 40) are
deliberately conservative.

### Getting a Nansen key

Sign up at **[nsn.ai/simmer](https://nsn.ai/simmer)**, then copy your key from
the Nansen dashboard. Signing up through that link gets you the Simmer partner
discount on your subscription and API credits.

**What a run costs.** Credit prices measured directly against the live API on
2026-08-11:

| Endpoint | Credits |
|---|---|
| `pnl-by-market` (primary signal, once per run) | 5 |
| `top-holders` | 5 |
| `address-summary`, `pnl-by-address`, `trades-by-address`, `market-screener` | 1 |
| `account` (balance check) | 0 |

At the default caps a full run costs **about 45 credits**. Note that Nansen's
published price list currently shows `pnl-by-market` at 1 credit; we are billed
5, so budget from the table above rather than from their docs.

**Which plan you need.** Nansen's free tier gives 100 starter credits plus 10
per day, which covers roughly two full runs and is enough to try the skill. For
regular use, Pro (2,000 credits per month) covers about 45 runs. Check your
balance any time with `GET /api/v1/account`, which is free.

Rank leaders for a market:

```bash
python3 nansen_skill_cli.py copytrader-overlay \
    --market-id 12345 \
    --leaders leaders.json \
    --top-n 5
```

`leaders.json`:

```json
[
  {"proxy_address": "0xabc...", "owner_address": "0xowner...", "wallet_score": 0.8}
]
```

World Cup market variant (adds WC-specialist bonus to the secondary signal):

```bash
python3 nansen_skill_cli.py copytrader-overlay \
    --market-id 12345 --leaders leaders.json --worldcup
```

Experimental live insider scan (always dry-run — see below):

```bash
python3 nansen_skill_cli.py insider-scan --market-id 12345 67890
```

Programmatic use (what `copytrading_strategy.py` would actually call):

```python
from nansen_copytrader_overlay_general import enrich_leaders

enriched = enrich_leaders(
    leaders=raw_leaders,           # [{proxy_address, owner_address, wallet_score}, ...]
    market_id=target_market_id,    # the market Simmer is about to copytrade into
    dry_run=not is_live,
)
target_wallets = [l["proxy_address"] for l in enriched[:top_n]]
```

## Proxy vs. owner wallets — must be explicit

Nansen indexes Polymarket activity (`prediction-market/...`) by the
**proxy** wallet (the contract Polymarket trades through), and profiler
data (`profiler/...`) by the **owner** (signing/EOA) wallet.
`address-summary` is a prediction-market endpoint, so it takes the
**proxy** — passing the owner returns an all-zero row.

`owner_address` is optional on input: it rides along on every
`pnl-by-market` row and is read from there when omitted. That harvest
covers only about **40% of the leaderboard** — 30 of 50 rows on the market
used to verify this carried the placeholder `"0x"` rather than a real
owner, which is treated as absent.

| Field | Used for | Endpoints |
|---|---|---|
| `proxy_address` (or legacy `address`) | Polymarket-specific data | `pnl-by-market`, `pnl-by-address`, `trades-by-address`, `top-holders`, `address-summary` |
| `owner_address` (optional) | General on-chain wallet quality | `historical-balances` |

A leader missing `proxy_address` is tagged `MISSING_PROXY_ADDRESS` and
skipped (kept at its original score, discounted). Missing `owner_address`
is tagged `MISSING_OWNER_ADDRESS` for the record but does **not** gate the
secondary signal — every call in that path is proxy-keyed.

## Credit guards (hard, not advisory)

Nansen bills per call against **your** key, so every enrichment run is
protected by:

- **`max_wallets`** (default 30): leaders beyond the cap are never
  enriched — they keep their original score, tagged
  `CREDIT_GUARD_MAX_WALLETS`.
- **`CreditGuard(max_calls=...)`** (default 40): a hard ceiling on live
  Nansen calls for the whole run, with a 5-minute TTL cache so re-fetching
  the same market/wallet doesn't double-spend. If the budget runs out
  mid-run, remaining leaders are tagged `CREDIT_GUARD_EXHAUSTED` and kept
  at their original score — the run does not crash, and it does not keep
  spending.
- **No `profiler labels` wrapper exists**, so nothing here can call it
  (100 credits for common labels, 500 for premium). The live insider scan's
  known-entity discount uses a free local allowlist instead.
- **`address_summary` as a pre-filter**: a wallet only gets the more
  expensive `pnl_by_address` + `trades_by_address` pull if its cheap
  `address_summary` win-rate/resolved-count check clears
  `MIN_RESOLVED_MARKETS` first.

Tune both via the CLI (`--max-wallets`, `--max-calls`) or by passing
`max_wallets=` / `guard=CreditGuard(max_calls=...)` directly.

## Live insider scan — experimental, dry-run only

`nansen_live_insider_scan.py` / `insider-scan` is **not launchable for live
trading**. `scan_live_markets(dry_run=False)` and `--live` both hard-refuse
(`LiveTradingNotSupportedError`), because two things are unconfirmed
against the live API:

1. **Owner/proxy handling**: scanned wallets come from PM trade data (proxy
   addresses), but `historical_balances` is a profiler/owner-indexed
   endpoint — `wallet_age_days` may be silently wrong until there's an
   owner mapping for scanned wallets.
2. **`HIGH_NO_ENTRY` price semantics**: whether Nansen quotes NO-side
   trades in YES terms or the NO token's own price changes whether this
   flag means conviction or the opposite. Unconfirmed — see the module
   docstring.

Removing the guard requires a deliberate code change after both are
verified — not a flag flip.

## Tests

```bash
source .venv/bin/activate
pytest tests/ -v
```

## Status

This skill ships as **research tooling, not an allocation signal.** The
scoring weights (the 50/50 blend, `MIN_RESOLVED_MARKETS`, the tag penalties)
are reasoned defaults. They have not been fitted or backtested, and no
forward test has shown that the re-ranking improves realised copytrading
returns. Read `DISCLAIMER.md` before acting on the output.

## Credits

Built by **Alyna Takahashi** ([@alyna123t](https://github.com/alyna123t)),
who wrote the adapter, both overlays and the insider scan. Code review by
**Nick** ([@BridgeAISocial](https://github.com/BridgeAISocial)). Published and
maintained by Simmer. Nansen provides the underlying Polymarket data.