# Hyperliquid bot safety refactor pattern

This reference captures the reusable lessons from hardening a private Hyperliquid crypto trading bot repository.

## Repository / GitHub workflow

- Clone private repos with a local token file, but keep `origin` token-free.
- If Bash token handling is brittle because of shell quoting or agent redaction, use a Python-created temporary `GIT_ASKPASS` helper.
- After push, verify `git rev-parse HEAD` equals authenticated `git ls-remote origin refs/heads/<branch>`.
- A token can push successfully but still fail PR creation with `403 Resource not accessible by personal access token`; in that case report the `/pull/new/<branch>` URL rather than retrying insecurely.

## Safety refactor slices

### Slice 1: Safety rails

Files/patterns:

- `config.py`: typed `BotConfig` and `RuntimePaths`.
- `execution.py`: `TradingExecutor(exchange, dry_run=True)` boundary.
- `strategy_config.json`: defaults `dry_run=true`, `tls_verify=true`, runtime path, max trades/loss settings.
- `.gitignore`: excludes `.env`, logs, CSVs, JSONL journals, reports, cooldowns, account health, portfolio state.
- Remove runtime data from Git index with `git rm --cached` rather than deleting local operational history unnecessarily.

Verification:

```bash
python3 -m pytest -q
python3 -m py_compile *.py
git diff --check
```

Scan for:

- `verify=False`
- `except Exception: pass`
- stale hardcoded workspace paths
- token-bearing remotes

### Slice 2: Strategy / risk / panic guard

Files/patterns:

- `strategy.py`:
  - `should_enter_flash_crash(...) -> EntryDecision`
  - `exit_long_position(...) -> ExitDecision`
  - `PositionState`
- `risk.py`:
  - `risk_gate(daily_pnl_usd, daily_trade_count, kill_switch_path, cfg) -> RiskDecision`
- `panic_guard.py`:
  - `expected_confirmation(wallet) -> "CLOSE ALL <last4>"`
  - `validate_panic_confirmation(...)`
- `panic_close.py` requires `--confirm "CLOSE ALL <wallet-last4>"` before any close orders.

Test cases to include:

- flash-crash entry allowed for qualified dump
- cooldown blocks entry
- dead-fish time stop closes long
- hard stop closes long
- kill switch blocks entries
- daily loss blocks entries
- panic confirmation must be exact and wallet-specific

### Slice 3: Journal / daily metrics / alert plans

Files/patterns:

- `journal.py`:
  - `append_journal_event(...)`
  - `read_journal_events(...)`
- `daily_metrics.py`:
  - `calculate_daily_metrics(journal_path, now=...)`
  - count only today's closed live trades
  - ignore dry-run events for live PnL
- `alerting.py`:
  - `AlertPlan(text, destination_topic="Crypto Trading Bot", side_effects=())`
  - `build_trade_alert(...)`
  - `build_risk_alert(...)`

AutoTrader integration:

- Append journal events on entry and exit.
- Use daily metrics to feed `risk_gate`, not placeholder values.
- Log alert plans first; actual Telegram sending is a later explicit adapter.

### Slice 4: Telegram adapter / paper exchange

Files/patterns:

- `telegram_alerts.py`:
  - `TelegramAlertConfig.from_env()` reads explicit enable flag, bot token, chat ID, and topic/thread ID.
  - `send_telegram_alert(alert, cfg, sender=...) -> TelegramSendResult` defaults to no send when disabled.
  - Missing topic/thread ID returns a closed failure such as `missing_thread_id`; do not silently send to the parent chat.
  - Tests should assert the bot token is only in the URL, never in payload/loggable alert text.
- `paper_trading.py`:
  - `PaperExchange(state_path)` implements the subset used by the bot: `set_leverage`, `create_order`, `fetch_positions`.
  - Persist positions/orders/leverage in runtime state, e.g. `~/.local/state/<BotName>/paper_state.json`.
  - Return a CCXT-like position shape so the main loop can reuse normal position handling.

AutoTrader integration:

- `CTB_TELEGRAM_ALERTS=false` by default; enabling Telegram also requires `CTB_TELEGRAM_CHAT_ID` and `CTB_TELEGRAM_THREAD_ID`.
- `paper_trading=true` swaps order/position execution to `PaperExchange` while still allowing live market-data reads.
- Keep `dry_run=true` as the safest default. For paper trading, use `CTB_PAPER_TRADING=true CTB_DRY_RUN=false`; dry-run off permits execution, paper-trading redirects execution to local state.

## Operational flags

Examples:

```bash
# Default / safe
python AutoTrader.py

# Explicit live mode
CTB_DRY_RUN=false python AutoTrader.py

# Block new entries immediately
touch ~/.local/state/CryptoTradingBot/KILL_SWITCH

# Re-enable entries
rm ~/.local/state/CryptoTradingBot/KILL_SWITCH

# Panic close with explicit wallet-specific confirmation
python panic_close.py --confirm "CLOSE ALL <wallet-last4>"

# Paper trading with live market data and local simulated fills
CTB_PAPER_TRADING=true CTB_DRY_RUN=false python AutoTrader.py

# Paper trading with Telegram alerts to a specific topic/thread
CTB_PAPER_TRADING=true \
CTB_DRY_RUN=false \
CTB_TELEGRAM_ALERTS=true \
CTB_TELEGRAM_BOT_TOKEN=<bot-token> \
CTB_TELEGRAM_CHAT_ID=<chat-id> \
CTB_TELEGRAM_THREAD_ID=<topic-id> \
python AutoTrader.py
```

## Notes

- Daily loss limits should use realized PnL from the journal, not unrealized position swings unless the strategy explicitly requires that.
- Entry/exit alerts should be side-effect-free plans until an adapter is tested and explicitly enabled.
- Keep raw amounts, logs, reports, wallet/API keys, CSVs, and account identifiers out of chat and Git unless explicitly sanitized.
