---
title: "Wallet Signals"
description: "Per-wallet directional signals, multi-wallet consensus, and auto-discovery with 4-component signal decomposition."
icon: "radar"
---

<Warning>
  **Ultra Feature.** Requires an Ultra subscription. [Get started at api.mathematicalcompany.com](https://api.mathematicalcompany.com)
</Warning>

# Wallet Signals

Unified entry point for tracking wallets and extracting directional signals. Combines wallet scoring, position analysis, trade flow, and behavioral profiling into a single actionable signal per wallet per market.

<CardGroup cols={3}>
  <Card title="Track Wallet" icon="user">
    Given a wallet, compute signals for every market it holds.
  </Card>
  <Card title="Consensus" icon="users">
    Aggregate top holders into a weighted directional signal.
  </Card>
  <Card title="Auto-Discover" icon="magnifying-glass">
    Find high-signal wallets in a target market automatically.
  </Card>
</CardGroup>

---

## Quick Start

### Mode 1: Track a Wallet

```python
import horizon as hz

signals = hz.track_wallet("0x1234...")
for s in signals:
    print(f"{s.market_id[:12]} signal={s.signal:.4f} regime={s.regime}")
```

### Mode 2: Market Consensus

```python
consensus = hz.compute_consensus("0xcondition...", market_price=0.65)
print(f"Signal: {consensus.signal:.4f}")
print(f"Agreement: {consensus.agreement:.2f}")
print(f"Wallets: {consensus.n_wallets}")
```

### Mode 3: Auto-Discover

```python
consensus = hz.scan_wallets(market_id="0xcondition...")
if consensus.is_actionable:
    print(f"Edge: {consensus.edge_bps:.0f} bps")
```

### Pipeline Integration

```python
hz.run(
    markets=[market],
    pipeline=[
        hz.wallet_tracker(wallets=["0xaaa...", "0xbbb..."]),
        my_quoter,
    ],
)

# In your quoter:
def my_quoter(ctx):
    sig = ctx.params.get("wallet_tracker_signal")
    if sig and sig.is_actionable:
        # Use sig.signal as directional bias
        ...
```

---

## Signal Math

Each per-wallet signal is a 4-component decomposition:

### Direction D(w,m)

Net position direction from wallet positions. YES positions contribute +1, NO contributes -1, size-weighted.

```
net = sum(size_i * direction_i) / total_size
D = (net + 1) / 2    # maps [-1, 1] to [0, 1]
```

### Conviction C(w,m)

Position size in this market relative to the wallet's average position across all markets.

```
ratio = market_size_usdc / avg_position_size_usdc
C = (tanh(ratio - 1) + 1) / 2
```

When ratio = 1 (average-sized position), C = 0.5. Larger than average positions push C toward 1.

### Recency R(w,m)

Exponential decay from the most recent trade, using the Rust `decay_weight()` function.

```
R = decay_weight(age_secs, half_life)
```

Default half-life is 6 hours. No trades = R = 0.0, which kills the signal entirely.

### Momentum M(w,m)

Volume-weighted buy ratio shift between the recent half and older half of trades.

```
M = 0.5 + (recent_buy_ratio - old_buy_ratio)
```

Detects direction changes. Needs 3+ trades; returns 0.5 otherwise.

### Composition

```
deviation = D - 0.5
amplified = deviation * C * 2
with_momentum = amplified + (M - 0.5) * momentum_weight
decayed = with_momentum * R
quality_adjusted = decayed * Q
S = clamp(0.5 + quality_adjusted, 0.01, 0.99)
```

Where Q is the wallet's `composite_score` from `score_wallet()`. Negative Q inverts the signal (fade bad wallets).

---

## Consensus Aggregation

For multi-wallet consensus:

1. Fetch top 20 holders via `get_top_holders()`
2. Score each wallet via `score_wallet()` (cached 5 min)
3. Filter by `|composite_score| >= min_wallet_quality`
4. Compute `WalletMarketSignal` for each
5. Weight: `|quality| * sqrt(position_size) * recency`
6. Weighted average of signals
7. Agreement = `1 - CV(signals)` (coefficient of variation)
8. Confidence interval from IQR of individual signals

Requires 3+ wallets by default; returns neutral otherwise.

---

## Pipeline Mode

The `wallet_tracker()` factory creates a pipeline function that:

- Refreshes signals every N cycles (default: 20)
- EMA-smooths signal between refreshes
- Detects regime changes and emits `WalletRegimeChange` events
- Supports both explicit wallet lists and auto-discovery

### Injected Context Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `wallet_tracker_signal` | `WalletMarketSignal` | Best signal for current market |
| `wallet_tracker_consensus` | `ConsensusSignal` | Multi-wallet consensus (if available) |
| `wallet_tracker_edge_bps` | `float` | Signed edge in basis points |
| `wallet_tracker_regime_changes` | `list[WalletRegimeChange]` | Regime flips detected this cycle |

---

## Oracle Integration

The wallet tracker can optionally feed into the Oracle as a 7th signal:

```python
config = hz.OracleConfig()
config.signal_weights["wallet_consensus"] = 0.20

fc = hz.forecast_market(
    "0xcondition...",
    market_price=0.65,
    config=config,
    include_wallet_signal=True,
)
```

The `wallet_consensus` signal weight defaults to 0.0 (disabled). You must explicitly set a non-zero weight and pass `include_wallet_signal=True`.

---

## Configuration Reference

```python
@dataclass
class TrackerConfig:
    decay_half_life_secs: float = 21600.0     # 6h for recency decay
    ema_alpha: float = 0.3                     # signal smoothing
    min_signal_strength: float = 0.05          # |signal - 0.5| threshold
    min_confidence: float = 0.3
    min_edge_bps: float = 200.0
    min_wallets_for_consensus: int = 3
    min_wallet_quality: float = 0.1            # smart money filter
    momentum_weight: float = 0.2              # max momentum shift
    regime_change_threshold: float = 0.4
    wallet_score_cache_ttl: float = 300.0      # 5 min
    poll_interval_cycles: int = 20
```

---

## Types Reference

### WalletMarketSignal

| Field | Type | Description |
|-------|------|-------------|
| `wallet` | `str` | Wallet address |
| `market_id` | `str` | Market condition ID |
| `signal` | `float` | Composite signal [0.01, 0.99] |
| `direction` | `float` | Position direction D(w,m) |
| `conviction` | `float` | Relative sizing C(w,m) |
| `recency` | `float` | Decay weight R(w,m) |
| `momentum` | `float` | Trade direction shift M(w,m) |
| `wallet_quality` | `float` | Composite score [-1, 1] |
| `confidence` | `float` | Signal confidence [0, 1] |
| `confidence_low` | `float` | Lower bound |
| `confidence_high` | `float` | Upper bound |
| `trade_count` | `int` | Trades in this market |
| `position_size_usdc` | `float` | Position value in USDC |
| `regime` | `str` | bullish / bearish / neutral / flipping |
| `edge_bps` | `float` | Edge vs market price |
| `is_actionable` | `bool` | Passes all thresholds |

### ConsensusSignal

| Field | Type | Description |
|-------|------|-------------|
| `market_id` | `str` | Market condition ID |
| `signal` | `float` | Weighted consensus [0.01, 0.99] |
| `confidence` | `float` | Aggregate confidence |
| `agreement` | `float` | Wallet agreement [0, 1] |
| `n_wallets` | `int` | Contributing wallets |
| `edge_bps` | `float` | Consensus edge |
| `wallet_signals` | `list[WalletMarketSignal]` | Individual signals |
| `best_wallet` | `str` | Highest-confidence wallet |
| `is_actionable` | `bool` | Passes thresholds |

### WalletRegimeChange

| Field | Type | Description |
|-------|------|-------------|
| `wallet` | `str` | Wallet address |
| `market_id` | `str` | Market condition ID |
| `old_regime` | `str` | Previous regime |
| `new_regime` | `str` | New regime |
| `confidence` | `float` | Detection confidence |
