# Hyperliquid Testnet strategy harness pattern

Use this reference when the user asks to push a pure v76/strategy candidate through the real Hyperliquid Testnet execution pipeline without any Mainnet order path.

## Scope and safety boundary

- Testnet signed actions only: require `CTB_HL_ENV=testnet` and `CTB_TESTNET_TRADING_ALLOWED=true` before placing any order.
- Mainnet is read-only only: market data, reconciliation, and paper/scorecard. Never load Mainnet signed credentials for strategy smokes.
- Keep strategy code pure: strategy returns an `OrderIntent`; all exchange calls live in tool/executor boundaries.
- Legacy AutoTrader must not be used for live/Testnet proof unless explicitly scoped; prefer `src.tools.*` harnesses.
- Treat `scheduleCancel` Testnet volume-limit responses as `conditional_unavailable_due_to_exchange_volume_limit`, not as a bot failure and not as green.

## Evidence update pattern

When previous successful signed Testnet probes exist, update preflight evidence with distinct gates:

- `testnet_credential_gate=true`
- `testnet_readonly_reconcile=true`
- `testnet_alo_cancel=true`
- `testnet_fill_stop=true`
- `testnet_final_cleanup=true`
- `testnet_schedule_cancel="conditional_unavailable_due_to_exchange_volume_limit"`

Preflight should surface `conditional_gates=["testnet_schedule_cancel"]` while keeping live-preview blocked until strategic/paper/alert/live-confirmation gates pass.

## Forced strategy smoke pattern

A good forced-signal smoke should prove the complete technical path, not profitability:

1. Load Testnet env only and print only masked addresses.
2. Generate a synthetic strategy-compatible market context that triggers the pure strategy.
3. Strategy returns a complete `OrderIntent`; journal it before execution.
4. Assert/record `strategy_exchange_calls=0` or equivalent boundary proof.
5. Run `PretradeRiskGate` with max notional, max risk, max open positions, required stop, and DataQualityGate results.
6. Execute entry through Testnet executor/tool boundary.
7. Detect fill from exchange state.
8. Immediately place reduceOnly stop-loss.
9. Reconcile with a local entry journal record so the reconciler can distinguish expected exposure from orphan exchange exposure.
10. Confirm stop present, reduceOnly, size correct, and direction correct.
11. Close the position deliberately.
12. Cancel remaining orders.
13. Final reconcile must show zero position and zero unexpected open orders.
14. Save a JSON report under `runtime/reports/`.

## Reconciler quirk

Hyperliquid Testnet open-order payloads for protective trigger stops can expose only:

- `reduceOnly: true`
- `side: "A"`/`"B"`
- `sz`/`origSz`
- `limitPx`

They may omit literal `orderType=trigger`, `stop`, or `tpsl`. For the missing-stop gate, treat reduce-only open orders as protective candidates, then still validate size and direction. Map Hyperliquid side `A`/`ask` to sell and `B`/`bid` to buy.

## Short bounded session pattern

After a forced smoke succeeds, a short natural-signal Testnet session should be conservative:

- Default first session coins: BTC, ETH, SOL; exclude WLD.
- Long-only; no shorts.
- No averaging down or martingale.
- Max open positions = 1.
- No automatic retry after error.
- After every trade, reconcile.
- If a position exists without a stop, stop the session immediately.
- If API health is degraded, block new entries.
- If cleanup fails, stop the session.
- If no natural signal appears, report blocked/no-signal per coin and do not manufacture trades.

## Paper/Mainnet parallel path

For v76 Mainnet-paper progress:

- Mainnet market data only; PaperExecutor only.
- Use net cost model and scorecard fast path.
- Report explicitly if no v76 PaperRuntime/experiment is currently registered rather than implying it is running.
- Keep live-preview red until there are at least 30 final v76 paper trades and the user’s net PF/PnL/coin-distribution/Telegram gates pass.

## Telegram alert smoke pattern

Alert smoke tools should build/send these event classes without secrets:

- info
- trade proposal
- entry
- stop placed
- exit
- critical reconcile
- kill-switch

If Telegram env is disabled or credentials/topic are absent, return `status=skipped` with per-alert planned results; do not fake a send.