Metadata-Version: 2.4
Name: hip4
Version: 0.1.0
Summary: Python SDK for Hyperliquid HIP-4 prediction markets
License: BUSL-1.1
License-File: LICENSE
Keywords: hyperliquid,hip4,hip-4,prediction-markets,binary-outcomes,eip712,sdk,web3
Author: Dennis Furrer
Requires-Python: >=3.10,<4.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Provides-Extra: async
Requires-Dist: eth-account (>=0.10.0,<0.14.0)
Requires-Dist: eth-utils (>=2.1.0,<6.0.0)
Requires-Dist: httpx (>=0.27.0,<0.28.0) ; extra == "async"
Requires-Dist: msgpack (>=1.0.5,<2.0.0)
Requires-Dist: requests (>=2.31.0,<3.0.0)
Requires-Dist: websocket-client (>=1.5.1,<2.0.0)
Requires-Dist: websockets (>=13.0,<14.0) ; extra == "async"
Project-URL: Homepage, https://github.com/perps-studio/hip-4-py
Project-URL: Repository, https://github.com/perps-studio/hip-4-py
Description-Content-Type: text/markdown

<h1 align="center">hip4-python</h1>

<p align="center">
  <a href="https://pypi.org/project/hip4/"><img src="https://img.shields.io/pypi/v/hip4.svg" alt="PyPI version" /></a>
  <img src="https://img.shields.io/pypi/pyversions/hip4" alt="Python versions" />
  <img src="https://img.shields.io/pypi/l/hip4" alt="license" />
</p>

---

Python SDK for Hyperliquid HIP-4 prediction markets. Sync and async clients, with EIP-712 signing handled internally.

This is the Python port of [`@perps/hip4`](https://github.com/perps-studio/hip-4). The TypeScript SDK remains the reference implementation; this port mirrors its public surface, types, and signing behaviour.

## Install

```bash
pip install hip4               # sync (requests + websocket-client)
pip install hip4[async]        # also installs httpx + websockets for the async client
```

```python
from hip4 import create_hip4_adapter

adapter = create_hip4_adapter()  # testnet by default
adapter.initialize()
events = adapter.events.fetch_events(active=True, limit=20)
```

For async, use `hip4.aio.create_async_hip4_adapter` with `async with`.

## Examples

- [`auth_eoa.py`](examples/auth_eoa.py) – Agent key approval and auth setup
- [`get_all_markets.py`](examples/get_all_markets.py) – Fetch all markets grouped by type
- [`place_limit_order.py`](examples/place_limit_order.py) – Limit order with price validation
- [`place_market_order.py`](examples/place_market_order.py) – Market order with `FrontendMarket` TIF
- [`stream_prices.py`](examples/stream_prices.py) – Stream live prices via WebSocket
- [`async_get_all_markets.py`](examples/async_get_all_markets.py) – Async market fetch

## Quickstart (sync)

```python
import os
from hip4 import LocalSigner, create_hip4_adapter
from hip4.types import PredictionOrderParams

adapter = create_hip4_adapter()
adapter.initialize()

# Agent key (separate from the user's wallet - sign with the user's wallet
# once via auth_eoa.py to approve, then store the agent key for trading).
agent = LocalSigner(os.environ["HIP4_AGENT_PRIVATE_KEY"])
adapter.auth.init_auth(os.environ["HIP4_USER_ADDRESS"], agent)

result = adapter.trading.place_order(PredictionOrderParams(
    market_id="123",
    outcome="#1230",
    side="buy",
    type="limit",
    price="0.5",
    amount="20",
    time_in_force="GTC",
))
print(result)  # PredictionOrderResult(success=True, order_id="...", ...)

adapter.destroy()
```

## Quickstart (async)

```python
import asyncio
from hip4.aio import create_async_hip4_adapter

async def main():
    async with create_async_hip4_adapter() as adapter:
        await adapter.initialize()
        events = await adapter.events.fetch_events(active=True, limit=20)
        print(len(events), "active events")

asyncio.run(main())
```

## API

The Python API is a snake_case mirror of the TypeScript SDK. All sub-adapters live on the top-level `HIP4Adapter` (sync) / `AsyncHIP4Adapter` (async) facade.

### `adapter.events`

| Method | Description |
|--------|-------------|
| `fetch_events(category, active, limit, offset, query)` | List events with optional filters |
| `fetch_event(event_id)` | Single event by ID |
| `fetch_categories()` | Available categories |
| `fetch_markets(params)` | Typed HIP-4 markets with optional grouping by type or question |
| `fetch_settled_outcome(outcome_id)` | Settlement details for a resolved outcome |
| `ensure_side_names()` | Pre-load side-name mappings |
| `get_side_name_resolver()` | Returns a sync resolver for side names by outcome ID |

### `adapter.market_data`

| Method | Description |
|--------|-------------|
| `fetch_order_book(market_id, side_index=0)` | L2 snapshot |
| `fetch_price(market_id)` | Both sides, 5s cache |
| `fetch_trades(market_id, limit=50, side_index=0)` | Recent trades |
| `fetch_candles(market_id, interval, start_time, end_time)` | OHLCV candles |
| `subscribe_order_book(market_id, on_data)` | Real-time L2 book |
| `subscribe_price(market_id, on_data)` | Real-time prices |
| `subscribe_trades(market_id, on_data)` | Real-time trades |
| `subscribe_all_mids(on_data)` | Bulk mid-price updates |
| `subscribe_active_asset_ctx(coin, on_data)` | Spot asset context |
| `subscribe_perp_asset_ctx(coin, on_data)` | Perp asset context |

Subscriptions return an unsubscribe callable.

### `adapter.account`

| Method | Description |
|--------|-------------|
| `fetch_positions(address)` | Outcome positions with resolved side names |
| `fetch_activity(address)` | Fills, last 30 days |
| `fetch_balance(address)` | Spot balances (incl. USDH) |
| `fetch_open_orders(address)` | Resting orders |
| `subscribe_positions(address, on_data)` | Polls every 10 s |

### `adapter.trading`

| Method | Description |
|--------|-------------|
| `place_order(params)` | Place a market or limit order |
| `place_orders(params_list)` | Batch order placement |
| `cancel_order(params_list)` | Cancel one or more resting orders |
| `modify_order(params)` | Modify a resting order (preserves queue priority on size-only changes) |
| `split_outcome(...)`, `merge_outcome(...)`, `merge_question(...)`, `negate_outcome(...)` | HIP-4 share-conversion primitives |
| `schedule_cancel(time_ms)` | HL "dead-man's switch" |

### `adapter.wallet`

| Method | Signing | Description |
|--------|---------|-------------|
| `set_signer(signer)` | – | Set user wallet for EIP-712 ops |
| `buy_usdh(amount)` | L1 agent | Buy USDH on spot |
| `sell_usdh(amount)` | L1 agent | Sell USDH on spot |
| `transfer_to_spot(amount)` | EIP-712 | Perp → Spot |
| `transfer_to_perps(amount)` | EIP-712 | Spot → Perp |
| `withdraw(...)` | EIP-712 | Withdraw to external address |
| `usd_send(...)` | EIP-712 | Send USDC to another HL address |
| `spot_send(...)` | EIP-712 | Send arbitrary spot token |
| `send_asset(...)` | EIP-712 | Unified-account-compatible transfer primitive |

### `adapter.auth`

| Method | Description |
|--------|-------------|
| `init_auth(wallet_address, signer)` | Sets the active signer used by trading & USDH ops |
| `get_auth_status()` | `"disconnected" \| "pending_approval" \| "ready"` |
| `clear_auth()` | Reset auth state |

The signer must implement the `hip4.types.hl.Signer` protocol (`get_address()` + `sign_typed_data(domain, types, value)`). Use [`hip4.LocalSigner`](hip4/auth.py) for in-memory keys, or wrap your own custodian / hardware-wallet bridge.

## Market Types

`adapter.events.fetch_markets()` classifies every HIP-4 outcome into one of four types:

| Type | Description | Parsed Fields |
|------|-------------|---------------|
| `defaultBinary` | Recurring price markets (BTC > $67,250 1d) | `underlying`, `target_price`, `expiry`, `period` |
| `labelledBinary` | Standalone binary with custom sides (Hypurr vs Usain Bolt) | Custom side labels |
| `multiOutcome` | Outcomes grouped under a question with fallback | `question_id`, `question_name`, `is_fallback` |
| `priceBucket` | Recurring multi-bucket price markets | `underlying`, `expiry`, `price_thresholds`, `period`, `lower_bound`, `upper_bound` |

```python
from hip4.types import FetchMarketsParams

# Filter by type
binaries = adapter.events.fetch_markets(FetchMarketsParams(type="defaultBinary"))

# Group by type
grouped = adapter.events.fetch_markets(FetchMarketsParams(group_by="type"))

# Group multi-outcome by question
by_question = adapter.events.fetch_markets(FetchMarketsParams(group_by="question"))
```

## Signing

Both Hyperliquid signing flows are implemented from scratch, using `msgpack`, `eth-utils.keccak`, and `eth-account` for EIP-712. They produce byte-for-byte equivalent signatures to the TypeScript SDK (verified by parity tests).

**L1 agent signing** (orders, cancels, USDH spot, share conversions, scheduleCancel):

1. Sort action keys in canonical order
2. MessagePack-encode
3. Append nonce as big-endian u64
4. Append vault marker byte
5. Keccak-256 hash → `connectionId`
6. EIP-712 sign with `Agent` type on chainId `1337`

**User-signed EIP-712** (transfers, withdrawals, sends):

- Domain: `HyperliquidSignTransaction`, `signatureChainId: 0x66eee` (testnet) or `0xa4b1` (mainnet)
- Message filtered to EIP-712 type keys only
- Requires the user wallet (agent keys are rejected)

The pure helpers (`sign_l1_action`, `sign_user_signed_action`, `sort_*`, `encode_msgpack`, `create_l1_action_hash`) are exposed at the top level for callers who want to sign outside the adapter.

## Development

```bash
poetry install --all-extras --with dev
make test          # pytest
make lint          # ruff + mypy
make format        # black + isort
```

Python 3.10+ is required (uses `kw_only` dataclasses and PEP 604 unions in some places).

## Acknowledgements

Signing implementation inspired by [`@nktkas/hyperliquid`](https://github.com/nktkas/hyperliquid) (TS) and the [official Hyperliquid Python SDK](https://github.com/hyperliquid-dex/hyperliquid-python-sdk).

## License

BUSL-1.1 - same as the TypeScript SDK.

