# Hyperliquid v76 technical-proof pattern

Use when proving the CryptoTradingBot Hyperliquid v76 architecture before any live-preview recommendation. This extends the safer modularization pattern with concrete proof steps from the 2026-06-07 session.

## Phase 0 — neutralize legacy live paths first

Before adding more strategy logic, scan legacy files for live-capable calls:

- `AutoTrader.py`, `AutoTrader_backup_*`, `Trader.py`, `panic_close.py`, `execution.py`
- terms: `create_order`, `market_open`, `market_close`, `ccxt.hyperliquid`, `Exchange`, `Info`

Hard guards:

- `AutoTrader.py`: default abort unless `CTB_ALLOW_LEGACY_AUTOTRADER=true`; message should be exactly recognisable, e.g. `Legacy AutoTrader disabled; use src/tools/preflight.py and v76 executors`.
- backup AutoTrader scripts: same guard; do not leave old backups capable of running orders by accident.
- `Trader.py`: default abort unless `CTB_ALLOW_LEGACY_TRADER=true`.
- legacy `execution.TradingExecutor`: dry-run remains allowed, but non-dry-run `set_leverage`/`create_order` requires `CTB_ALLOW_LEGACY_EXECUTION=true`.
- `panic_close.py`: keep it, but require explicit confirmation token; never print private keys or `.env` contents.

Add tests proving default blockade and dry-run preservation.

## Phase 1 — read-only Hyperliquid adapter

Keep read-only modules free from `Exchange`/order imports. Use Hyperliquid Info/read-only endpoints only.

Required adapter functions:

- `market_data.py`: `get_meta`, `get_meta_and_asset_ctxs`, `get_all_mids`, `get_candles`, `get_l2_book`, `get_asset_universe`, `get_symbol_meta`, `get_sz_decimals`.
- `account_state.py`: `get_user_state`, `get_spot_user_state`, `get_open_positions`, `get_open_orders`, `get_account_equity`, `get_free_usdc`, `get_margin_usage`.

Smoke command:

```bash
CTB_HL_READONLY=true .venv/bin/python -m src.tools.hl_readonly_smoke --coins BTC,ETH,SOL,LINK,WLD --json
```

It must save JSON to `runtime/reports/hyperliquid_readonly_smoke_YYYYMMDD_HHMMSS.json`, include coin presence, `szDecimals`, mid, 1m/5m candles, L2 best bid/ask/spread, and account-state readability — never secrets.

Do not put network smoke tests in the normal pytest pipeline; keep offline/mocked pytest green.

## Phase 2 — payload/testnet/dry-run proof

Implement `OrderIntent -> payload` conversion separately from submission:

- round size by `szDecimals` and price by Hyperliquid price rules before payload creation.
- check `size > 0`, min notional from rounded size/price, and stop-loss requirement for non-reduce-only entries.
- create reduce-only SL/TP payloads.
- journal accepted/rejected, reason, rounded size/price, estimated notional, CLOID, strategy id.

Smoke commands:

```bash
.venv/bin/python -m src.tools.hl_order_payload_smoke --coin BTC --side buy --notional 15 --dry-run
.venv/bin/python -m src.tools.hl_testnet_order_smoke --coin BTC --notional 15 --mode alo-cancel
.venv/bin/python -m src.tools.hl_deadman_smoke --env testnet --seconds 60
```

Mainnet must remain blocked. Testnet order and deadman smokes should skip safely unless `CTB_HL_ENV=testnet` and explicit testnet gates/confirmation are present.

## Phase 3 — production-like reconciliation

Reconciler must identify and report:

- exchange positions by coin, open orders by coin.
- reduce-only stop orders and TP orders.
- exchange position without local journal.
- local journal position without exchange position.
- position without stop.
- stop wrong size or wrong direction.
- position exposure above limit.
- open entry order without deadman protection.

If any position lacks a stop, return `block_new_entries=true`, `severity=CRITICAL`, and a `prepare_telegram_alert` recommended action. Do **not** auto-panic on mainnet. Testnet may prepare a dry-run repair plan.

Smoke command:

```bash
.venv/bin/python -m src.tools.hl_reconcile --env mainnet --readonly --json
```

It must be read-only and tolerate missing account-address env by returning a clear `skipped/address_missing` result.

## Phase 4 — realistic paper costs and scorecard

Add a `CostModel`/paper fill path that records:

- entry fee, exit fee, spread cost, slippage cost, funding cost.
- gross/net PnL, risk USD, R multiple where available.
- estimated roundtrip cost pct.
- block if expected move is less than `3x` roundtrip cost.

Extend the paper scorecard with net and concentration fields:

- `pnl_total_net`, `pnl_ex_wld`, `pnl_ex_top_coin`, `pnl_by_coin`, `trade_share_by_coin`.
- `top_coin_share_last_20`, `top_coin_share_last_30`.
- `profit_factor_net`, `max_drawdown_net`, `max_consecutive_losses`, `average_risk_per_trade`.
- `stops_missing_count`, `open_exposure_mismatch`.
- blocked counts: cost, spread, depth, coin leakage.

Existing old journals may lack cost fields; fallback to `gross - known costs` but mark future work to journal full cost fields.

## Verification checklist

Run before reporting completion:

```bash
.venv/bin/python -m pytest -q
.venv/bin/python -m compileall -q src AutoTrader.py Trader.py panic_close.py AutoTrader_backup_*.py execution.py tests
python3 - <<'PY'
import ast, pathlib
bad=[]
terms={'market_open','market_close','create_order','cancel','cancel_by_cloid','Exchange','Info'}
for path in pathlib.Path('src/strategies').glob('*.py'):
    tree=ast.parse(path.read_text())
    for node in ast.walk(tree):
        if isinstance(node, ast.Name) and node.id in terms:
            bad.append((str(path), node.id, node.lineno))
        if isinstance(node, ast.Attribute) and node.attr in terms:
            bad.append((str(path), node.attr, node.lineno))
print('strategy_exchange_calls', bad)
raise SystemExit(1 if bad else 0)
PY
```

Final report must include branch/SHA, changed files, focused/full tests, compileall, smoke results, safety status, remaining live-preview blockers, and explicitly state no live recommendation without new user approval.
