v1.0.0 to v2.0.0

123 added, 511 removed. Audit B to A.

---
name: pathiel-agent
- description: Use when operating, maintaining, or debugging pathiel — the standalone autonomous Hyperliquid trading system that Pathiel Agent drives through its MCP server. Covers the scan/research/execute pipeline, pluggable AI-brain providers, live EV+ strategy books, risk gates, MCP tool wiring, and Hyperliquid order-placement gotchas.
- version: 1.0.0
+ description: Use when operating, maintaining, or debugging pathiel — the headless Hyperliquid trading desk Claude trades through its MCP server. Covers place_bracket/bracket_sweep/close_position, the risk-profile sizing path, MCP tool wiring, and Hyperliquid order-placement gotchas.
+ version: 2.0.0
author: Pathiel Agent
license: MIT
metadata:
pathiel:
- tags: [trading, hyperliquid, mcp, autonomous, quant]
+ tags: [trading, hyperliquid, mcp, quant]
related_skills: [hyperliquid-agent-wallets]
homepage: https://github.com/Julian-dev28/pathiel
---
- # Pathiel-Trader Agent
+ # Pathiel Trading Desk
- `pathiel` is a **standalone Python trading system** for Hyperliquid
- perpetual markets — both native crypto perps (BTC, ETH, etc.) **and HIP-3
- tokenized-equity / commodity / index perps** (`xyz:NVDA`, `xyz:GOLD`,
- `km:US500`, `xyz:CL`, etc.) when the `enable_hip3` flag is on. Pathiel Agent
- operates it through the **MCP server** registered in `~/.pathiel/config.yaml`
- (`mcp_servers.pathiel`) — that MCP boundary is the integration. The
- trading engine itself has no Pathiel-framework dependency; it is
- Pathiel-*operated*, not Pathiel-*built*.
+ `pathiel` is a **headless Hyperliquid trading desk** (operator decision,
+ 2026-10-23): Claude is the only trader on the wallet, placing every trade
+ through the **MCP server** (`scripts/pathiel-mcp-server.py`, registered in
+ `~/.pathiel/config.yaml` under `mcp_servers.pathiel`) — `place_bracket`,
+ `bracket_sweep`, `close_position`. There is no autonomous scan/research/
+ execute engine any more: the old scan→research→execute pipeline, its
+ trigger config, TA filter, risk gates, DSL exit loop, and the seven
+ strategy books it used to run are all retired. See
+ `docs/overhaul/HEADLESS_2026-10-23.md` for the wave-1 (strategy-book)
+ retirement and its wave-2 addendum (the engine itself) for the full history,
+ and `.claude/skills/oil-tick/SKILL.md` for the 15-minute loop Claude
+ actually runs today.
Repo: `/Users/julian_dev/Documents/code/pathiel`. The user may work on
dirty local branches and switch deploy branches between sessions. **Commit/push
only when the user asks, and never push directly to another branch without
explicit confirmation.**
- ## Architecture
-
- A pipeline designed to keep AI token cost proportional to real opportunity:
+ ## How a trade happens now
- 1. **Scan** — fetch all mids (native + HIP-3 dexes when `enable_hip3=true`),
- evaluate 6 triggers per market (pctMoveSpike, volumeSpike, breakout,
- rangeCompression, trendStrength, momentumBurst). The candle-fetch budget
- is bucketed (default 45 core markets plus optional sweep): top-N crypto by volume + top-M crypto
- by `|24h%|` (movers) + top-K HIP-3 by volume, so HIP-3 tokenized equities
- and low-volume native big-movers each get scanned regardless of where
- they rank against the BTC/ETH volume leaders. `momentumBurst` bypasses
- the composite-score gate so explosive moves always surface. Every
- perception is persisted via `memory.record_perception`.
+ 1. **Read the call.** `desk_brief {desk}` (oil is primary) for the desk's
+ current thesis, or `desk_refresh` to force a new one. `risk_limits` /
+ `risk_size_trade` for what the account may trade and how big — size
+ every trade with `risk_size_trade`, never by hand.
+ 2. **Place it.** `place_bracket {coin, entry_px, stop_px, tp_px}` — one
+ entry with an exact stop and target. It sizes through
+ `services/risk/size.py`'s `size_trade()` against the operator's saved
+ risk profile, checks the stop against liquidation
+ (`pathiel.agents.bracket.check_leverage`), and refuses under the 1.5R
+ minimum or when a position/entry already exists on the coin.
+ 3. **Housekeep.** `bracket_sweep {coin}` after every close: cancels the
+ stranded sibling order left by a fired stop/target and confirms an open
+ position is covered by a stop.
+ 4. **Close.** `close_position {coin}` — market-closes with `reduce_only`,
+ cancels the coin's remaining SL/TP bracket, and reports the realized
+ PnL. Implemented in `pathiel/agents/bracket.py`'s `close_position()`
+ (moved there from the retired `executor.close_position_market`,
+ 2026-10-23 wave 2).
- Env knobs: `PATHIEL_MAX_MARKETS`, `PATHIEL_MAX_MARKETS_HIP3`,
- `PATHIEL_MAX_MARKETS_MOVERS`, `PATHIEL_UNIVERSE_SWEEP`,
- `PATHIEL_SCAN_WORKERS`, `PATHIEL_BATCH_SIZE`, `PATHIEL_BATCH_SLEEP`,
- `PATHIEL_MOVERS_VOL_FLOOR_USD`, `PATHIEL_HIP3_MOVERS_FLOOR_USD`.
- 2. **Pre-research cooldown** — `trading_loop.py` checks the most recent
- trade per coin and skips paid AI research if the coin is still inside its
- `cooldown_min` window. The execute-time `cooldown_gate` remains as the
- authoritative backstop.
- 3. **TA Filter** — `ta_filter.py` does multi-timeframe validation (1h/4h/1d
- EMA, RSI, ATR, ADX, volume) at zero AI cost. Only CONFIRMED perceptions
- (score ≥ 45) reach AI research; WEAK / REJECTED are dropped. A perception
- whose `momentumBurst` trigger fired bypasses the gate.
- 4. **AI Research** — deep AI analysis via the selected brain provider
- (`openrouter`, `claude_cli`, or `codex_cli`) on triggered candidates. The
- provider returns verdict text only; `parse_verdict()` and the executor path
- are shared.
- 5. **Execution** — ATR equal-risk sizing when `atr_risk_sizing.enabled=true`
- (current live path), with fraction-based sizing as the explicit fallback.
- Orders are clamped by per-trade notional and leverage caps, normalized to
- Hyperliquid coin precision before gates, signed through the SDK, protected
- by a server-side backup stop-loss, and registered with DSL dynamic exits.
- Blocked attempts are NOT written to `memory._trades` — only successful
- executions appear there, so `cooldown_gate` keys off real history rather
- than its own rejection log.
+ Full tool list and safety annotations: `docs/overhaul/MCP.md`
+ (59 tools, generated by `scripts/mcp_tool_table.py` — regenerate it after
+ any tool change, never hand-edit).
- ## Running
+ ## Running the server
- Use `scripts/restart.sh` as the canonical process manager for the loop and API
- server:
+ Use `scripts/restart.sh` as the canonical process manager:
```bash
- scripts/restart.sh # restart both trading loop + FastAPI server
- scripts/restart.sh loop # restart trading loop only
+ scripts/restart.sh # restart server + log rotator
scripts/restart.sh server # restart FastAPI server only
scripts/restart.sh stop # stop both, don't start
scripts/restart.sh status # show what's running
```
- Logs land in `logs/trading_loop.log` and `logs/server.log`. The MCP server
- (`scripts/pathiel-mcp-server.py`) is intentionally NOT managed — it's a transient
- stdio process respawned by Pathiel Agent on each tool call. If MCP code is stale:
- `pkill -f pathiel-mcp-server.py` and the next tool call respawns fresh.
-
- When `restart.sh` is run from Codex and background children are reaped by the
- execution wrapper, launch the long-lived processes in `screen` instead:
-
- ```bash
- screen -dmS pathiel-server /bin/zsh -lc 'cd /Users/julian_dev/Documents/code/pathiel && PATHIEL_HL_RATE_REFILL_PER_SEC=5 PATHIEL_HL_RATE_CAPACITY=200 .venv/bin/python -m pathiel.server >> logs/server.log 2>&1'
- screen -dmS pathiel-loop /bin/zsh -lc 'cd /Users/julian_dev/Documents/code/pathiel && PATHIEL_STARTUP_GRACE_S=0 PATHIEL_META_PREWARM_TIMEOUT_S=3 .venv/bin/python scripts/trading_loop.py >> logs/trading_loop.log 2>&1'
- screen -ls
- curl -s -o /tmp/pathiel-dashboard.html -w "%{http_code}\n" http://localhost:8000/
- ```
-
- Manual foreground launch is only for debugging:
-
- ```bash
- python scripts/trading_loop.py # continuous scan -> research -> execute
- python -m pathiel.server # API server only
- ```
-
- The `--env prod --daemon` flags are parsed but **informational only** — the
- script does NOT actually fork or daemonize itself. The loop already has its own
- `while True` with periodic sleeps; use `scripts/restart.sh` when it needs to
- survive the terminal session.
-
- Cadence is `PATHIEL_SCAN_INTERVAL` (default 60s). The MCP server no longer
- exposes the bot's own scan/research/execute steps directly (retired
- 2026-10-23 — see MCP Integration below); driving the loop step by step means
- running `trading_loop.py` itself or calling `pathiel.agents.research`/
- `pathiel.agents.executor` in-process.
+ Logs land in `logs/server.log`. The MCP server
+ (`scripts/pathiel-mcp-server.py`) is intentionally NOT managed by
+ restart.sh — it's a transient stdio process respawned by the MCP client
+ on each tool call. If MCP code is stale: `pkill -f pathiel-mcp-server.py`
+ and the next tool call respawns it fresh.
- ### Restarting the Trading Loop + Server
+ Manual foreground launch is only for debugging: `python -m pathiel.server`.
- `scripts/restart.sh` handles stop (SIGTERM → SIGKILL fallback), verify,
- background start with logs, and a status readout. `status` may show the process
- group (`screen`/shell/python/`caffeinate`); the invariant is exactly one
- `python ... scripts/trading_loop.py` process. If logs show overlapping scan
- cadences, check for an orphan with `ps ax | rg "scripts/trading_loop.py"` and
- stop the older process before trusting live behavior.
+ There is no trading loop or scheduler process any more (both retired
+ 2026-10-23, wave 2, with the rest of the autonomous engine) — only the
+ dashboard/API server and the log rotator run as standing processes.
- **Keep-awake (laptop hosts):** `start_loop` now launches a `caffeinate -i -m -w $pid` alongside the loop so the host can't idle/maintenance-sleep mid-run. On a sleeping Mac the whole process freezes — you'll see `[watchdog] no progress for N s — HUNG` re-execs where N is *minutes-to-hours* (that's the sleep gap, NOT a code hang; the watchdog re-execs correctly on wake). During sleep only the **server-side SL/TP brackets** protect open positions. `caffeinate -i` stops idle sleep but a closed lid on battery can still clamshell-sleep → keep on AC for true 24/7. See `references/daemon-investigation.md`.
+ ## Risk sizing
- **Pitfall:** `python3 scripts/trading_loop.py --env prod --daemon` does NOT daemonize — the `--daemon` flag is parsed but has no effect. Use the restart script for persistent operation; only fall back to a manual launch if the script is unavailable.
+ Every trade sizes through `services/risk/size.py`'s `size_trade()`:
+ `notional = (equity × risk_per_trade_pct) / stop_distance`, clamped by the
+ operator's saved risk profile (`risk_limits` MCP tool / `/api/risk/*`) and
+ the coin's exchange max leverage. Leverage is already baked into the
+ notional — `position_notional = trade_notional`, never
+ `trade_notional × leverage` again. `pathiel.agents.bracket.min_tradable_equity`
+ is the structural floor below which the exchange can't clear a $10 order at
+ all; `services.risk.books.main_engine_spec` is the one `BookSpec` the
+ feasibility check (`risk_limits`/`risk_size_trade`/`place_bracket`) prices
+ against — there is no per-book roster any more.
- ## Asset-class toggles
+ ## Asset-class / dex toggles
- `.agent-config.json` carries two boolean flags that control what the scanner
- and executor will trade:
+ `.agent-config.json` carries:
- - `enable_crypto` (default `true`) — scan native HL perps (BTC, ETH, SOL, ...).
- - `enable_hip3` (default `true`) — scan HIP-3 perpDexes (xyz / vntl / km / ...).
+ - `enable_hip3` (default `true`) — includes HIP-3 perpDexes (xyz / vntl /
+ km / ...) in market reads.
+ - `hip3_dex_allowlist` / `hip3_dex_blocklist` — which HIP-3 dexes
+ `get_universe()` fetches at all (a cost control on market/desk reads, not
+ a per-trade safety gate — Claude names the coin explicitly on every
+ `place_bracket` call, so the discipline that used to be an autonomous
+ allowlist refusal is now the oil-tick skill's own scope).
+ - `coin_allowlist` / `coin_blocklist` — bare-ticker majors restriction
+ (`pathiel/agents/universe.py`), matched by bare ticker so one entry
+ covers every venue prefix for the same underlying.
- Both false = no-op scan (logged loudly). Single-class runs hand the full
- candle budget to that class. The executor enforces the same gating at
- execute-time so stale perceptions can't sneak through if the flag flips
- mid-cycle (`hip3_disabled` / `crypto_disabled` reasons).
+ 2026-10-23 (wave 2) dropped `enable_crypto` and every per-trade sizing/
+ gating knob the retired autonomous engine read (leverage, equity-fraction,
+ the confidence/volume/regime gates, `dsl_exit`, `runner_entry_gate`, ...) —
+ see `pathiel/agents/config_store.py`'s `DEFAULT_CONFIG` for the full list
+ of what left and why.
## AI Brain Providers
- Research has one provider seam:
-
- ```
- research._call_ai(system_prompt, user_message)
- -> pathiel.agents.ai_brain.get_brain(provider).complete(...)
- -> parse_verdict(...)
- ```
-
- Valid providers are:
-
- - `openrouter` — default HTTP completion provider. Keeps the 402 affordability
- retry that shrinks `max_tokens` once when OpenRouter reports an affordable
- token budget.
- - `claude_cli` — `claude -p --output-format json` headless provider. The
- handler parses the envelope, rejects `is_error`, and passes `.result` to the
- shared verdict parser.
- - `codex_cli` — `codex exec --sandbox read-only --ephemeral -` provider. The
- handler passes stdout to the shared verdict parser.
-
- Selector precedence: `AI_BRAIN_PROVIDER` env var, then
- `.agent-config.json` → `ai_brain.provider`, then `openrouter`. The selector is
- hot-read per research call; code changes still require a loop restart.
-
- Failure contract is load-bearing: provider failure, timeout, non-zero exit,
- empty stdout, or output without verdict JSON returns `""`. That becomes
- `ai_down=True` PASS and the TA sidestep override must not upgrade it.
-
- CLI provider auth is outside Pathiel. Verify `claude -p` / `codex exec` works
- non-interactively in the loop's actual environment before switching live.
-
- ## MCP Integration
-
- Pathiel is a headless oil desk as of 2026-10-23: Claude trades through this
- MCP server, the autonomous bot's own brain path is retired, and the operator
- trades his own wallet by hand from Claude's thesis. See
- `.claude/skills/oil-tick/SKILL.md` for the loop Claude runs.
-
- The server (`scripts/pathiel-mcp-server.py`, stdio, 59 tools) is registered in
- `~/.pathiel/config.yaml`:
-
- ```yaml
- mcp_servers:
- pathiel:
- command: python3
- args:
- - /Users/julian_dev/Documents/code/pathiel/scripts/pathiel-mcp-server.py
- cwd: /Users/julian_dev/Documents/code/pathiel # recommended
- timeout: 60
- connect_timeout: 30
- env:
- OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
- ```
-
- Primary tools: `place_bracket`, `bracket_sweep`, `close_position`,
- `set_leverage`, `cancel_order`, `state`, `config`. Adding tools and the audit
- invariant: see `references/mcp-server.md`. After editing the server, restart
- it: `pkill -f pathiel-mcp-server.py` (the next call respawns it fresh).
-
- `close_position` delegates to `executor.close_position_market()`. Do not
- re-implement close logic in MCP handlers; that helper owns reduce-only close
- orders, DSL tracker cleanup, stale trigger-order cancellation, realized PnL
- capture, and loss-cooldown arming.
-
- `place_bracket` is the MCP-native entry seam: an agent (Claude, Codex, Pathiel
- Agent, or OpenClaw) names a coin, stop and target; the tool sizes it with
- `services.risk.size.size_trade` and places the entry with its stop and target
- attached through `pathiel.agents.bracket.place_bracket`, refusing under the
- R-multiple minimum or when the stop sits too close to liquidation for the
- requested leverage. `bracket_sweep` cancels a stranded sibling order and
- confirms an open position is covered by a stop; run it after every close.
+ `ai_brain` stays — `services/thesis`'s desk-refresh research and
+ `services/trend_engine` both call `pathiel.agents.ai_brain.get_brain()`.
+ Valid providers: `openrouter` (default HTTP), `claude_cli`
+ (`claude -p --output-format json`), `codex_cli` (`codex exec --sandbox
+ read-only --ephemeral -`). Selector precedence: `AI_BRAIN_PROVIDER` env
+ var, then `.agent-config.json` → `ai_brain.provider`, then `openrouter`.
## State Files
- Project state — not Pathiel memory (all gitignored):
- - `.agent-config.json` — mode (OFF/LIVE), AI brain provider, risk caps, thresholds
- - `.agent-memory.json` — perceptions, analyses, trades, cooldowns
- - `.data_funding_oi.jsonl` / `.data_logger_ts` — the dashboard's funding/OI
- (funding-heat) panel. Written by `data_logger` from inside the loop; stops
- growing whenever the loop is stopped
- - `auth.db` — users, sessions, login nonces (`services/auth`)
- - `supervisor_halt.json` — components deliberately stopped. `supervise_processes`
- refuses to restart anything listed here; every explicit start clears its own
- entry. Check this first when a restart appears to do nothing
- - `logs/trading_loop.log` and `logs/server.log` — process logs
-
- ## Headless Desk (2026-10-23)
-
- The autonomous strategy books are retired — operator decision: pathiel trades
- through this MCP server only, placed via `place_bracket` / `bracket_sweep`
- (`pathiel/agents/bracket.py`). `scripts/trading_loop.py` scans, reviews open
- positions, and manages exits; it places no entries of its own. There is
- exactly one trader on the wallet.
-
- Deleted with the books: `news_surge_short_live.py`, `news_surge_multi.py`,
- `social_trending_recorder.py`, `unlock_short_live.py`, `xs_reversal_live.py`,
- `drawdown_ladder_live.py`, `copy_trade_live.py`, their private helper
- (`book_helpers.py`), the cross-book claims registry
- (`rebalancer_owned.ClaimsRegistry`/`OwnedPositions` — `.rebalancer_claims.json`
- no longer exists), and the nightly grader (`scripts/autonomous_cycle.py`,
- `_SWITCHES`). Full account: `docs/overhaul/HEADLESS_2026-10-23.md`.
-
- What survived because it was never book-only: `book_params.py`'s
- sizing-floor resolver (now the one fallback every entry — book or not —
- sizes through), `shadow_ledger.py` (still grades the live
- `thin_short_relax` experiment), `rebalancer_owned.state_file()`,
- `capital_flows.py`, `data_logger.py`, `news_catalyst.py`, `deadline.py`.
-
- Do not reintroduce an autonomous book without a fresh EV+ audit, a written
- finding in `research/alpha_swarm/findings/`, and an explicit user request —
- the operator's standing decision is headless, not shadow-and-promote.
-
- ## Risk Gates (independent, no short-circuiting)
-
- Every gate is evaluated; results are collected even when one blocks:
- confidence, max_concurrent, per_trade_notional_cap, daily_loss_killswitch,
- **daily_giveback**, market_liquidity_floor (+ HIP-3 split), short_liquidity,
- coin_allowlist / coin_blocklist, cooldown, opposite_direction_guard,
- correlation_cap, equity_risk_cap, news_blackout, market_regime.
-
- Notes on specific gates:
- - **daily_giveback** (give-back breaker, 2026-06-06): once the day's PnL peaks
- ≥ `daily_giveback_min_peak_usd`, blocks NEW entries if it retraces >
- `daily_giveback_halt_pct` from that peak (existing positions ride their stops;
- resets at UTC roll). Locks green days from round-tripping. Uses the TRUE
- aggregate account PnL (not main-dex-only — that bug spuriously halted). `0` = off.
- - **market_regime**: blocks counter-trend trades unless `confidence ≥
- counter_regime_min_conf` OR `composite_score ≥ 50` ("via composite") OR a
- binary momentum trigger fired. Aligned trades clear at the lower
- `aligned_min_conf`. **`block_counter_trend_bypass`** (currently `true`)
- disables ONLY the binary-trigger bypass — the composite≥50 path stays open, so
- strong-momentum counter-trend trades (e.g. an alt long in a down regime via
- composite strength) still pass. **Crowded-squeeze caution** (`crowded_with_min_conf`):
- a with-the-crowd aligned trade (short+`SHORT_CROWDED` / long+`LONG_CROWDED`) no
- longer gets the free "aligned" pass — must clear that conf or it's blocked
- `via:crowded_squeeze` (those are the entries that get squeezed). Regime is
- computed from **1h candles, 8-bar lookback** each scan (fresh, not stale).
- - **short_liquidity**: a SEPARATE, deeper 24h-volume floor for SHORTS only
- (`min_short_volume_usd`, $50M) — thin markets squeeze. Distinct from the
- long/general `market_liquidity_floor`.
- - **correlation**: `max_crypto_long_correlated` caps simultaneous correlated
- crypto exposure (concentration guard).
- - **news_blackout**: skipped for tokenized-equity perps. Crypto + commodity gated.
-
- **Surfacing layer (what reaches research)** — beyond the weighted composite gate,
- several weight-0 "surfacing bypasses" bring a coin to the AI even below the gate;
- the AI + risk gates then adjudicate:
- - `uptrendMomentum` / `downtrendMomentum` — sustained intraday trend (both
- directions; the down side is what lets us short selloffs).
- - `dailyMover` — large liquid 24h movers. Raw `daily_move_pct` is carried into
- analysis so TA sidestep can block parabolic PASS upgrades.
-
- **Entry-side guardrails (2026-06, backtest-gated):**
- - **`late_chase_relax`** (LIVE) — narrows the runner gate's "late trend-only
- chase; no fresh breakout/burst" block. Admits trend-aligned no-breakout entries
- ONLY on liquid coins (vol ≥ `min_volume_usd`, default $5M) inside the
- `[min_ext_pct, max_ext_pct]` daily-extension band (20–30%) — the one pocket
- retained in the live config. Low-liquidity and out-of-band chases stay blocked.
- - **`capital_rotation`** (LIVE) — when a strong fresh candidate is blocked purely by
- capital (book full / notional / margin), evicts the weakest non-winner (roe <
- `protect_winner_roe_pct`, age ≥ `min_hold_minutes`) for it. Wired at both the
- executor stage and the pre-research margin preflight.
- - **`gex_signal`** (LIVE, HIP-3 only) — option gamma call-wall guardrail for
- tokenized equities. It is a veto, not standalone alpha: longs jammed under a
- nearby long-gamma call wall are blocked at the configured wall distance.
-
- **Config keys are read tolerantly** — current gates accept snake_case and the
- legacy camelCase form for common knobs. Prefer snake_case in `.agent-config.json`
- and MCP writes so diffs stay predictable.
-
- ## Trade Sizing
-
- Current live sizing uses `atr_risk_sizing`: target risk is
- `equity × risk_per_trade_pct`, converted to notional using the primary stop
- distance (`sizing_basis=primary_stop`) and then clamped by
- `max_trade_notional_usd`, configured leverage, and the coin's max leverage.
- When ATR sizing is disabled, the fallback is
- `equity_fraction_per_trade × equity × leverage`.
- It is bounded by `max_concurrent`,
- `max_total_notional_pct`, and `max_trade_notional_usd`.
-
- **Per-trade notional CLAMPS, not rejects** (`executor.py`): the computed
- `trade_notional` is clamped down to `max_trade_notional_usd` so an oversized
- candidate is sized down to the cap and can still be taken. The executor then
- normalizes to the exact exchange-valid coin size before gates; tiny precision
- dust around the cap is tolerated, while real overshoots are still blocked.
-
- Free-margin floor: the executor refuses if `available / equity <
- min_available_margin_pct` (config; currently **0.10**).
- `available` is `accountValue - totalMarginUsed` (matches HL "Available to
- Trade"). A defensive `equity_unavailable` reason fires when HL returns
- `equity=0` (transient outage) instead of sending an unsized order.
+ Project state (all gitignored):
+ - `.agent-config.json` — mode (OFF/LIVE), AI brain provider, risk profile,
+ HIP-3 dex allow/blocklist, coin allow/blocklist
+ - `.state/strategies/<desk>/{theses,trades}/` — every desk's thesis and
+ trade journal (`services/strategies/journal.py`, `trades.py`) — the
+ evidence base under every graded call; back it up (`scripts/
+ backup_state.py`) the same way `.agent-memory.json`/`shadow_ledger` used
+ to matter.
+ - `.state/capital_flows.jsonl` — deposits/withdrawals, for flow-neutral
+ drawdown.
+ - `auth.db` — users, sessions, login nonces (`services/auth`).
+ - `logs/server.log` — the dashboard/API process log.
- For HIP-3 trades the executor runs a per-dex preflight (queries that
- specific dex's clearinghouse) and refuses with `hip3_dex_underfunded` if
- the target dex has < $1 — HIP-3 dexes are separate clearinghouses and
- agent wallets cannot transfer between them.
+ `.agent-memory.json`, `.dsl-state.json`, `.rebalancer_claims.json` and
+ `.state/shadow_ledger/` no longer exist — their writers
+ (`pathiel.agents.memory`, `dsl_exit`, `rebalancer_owned`'s claims registry,
+ `shadow_ledger`) are all retired.
- ## Exit Engine (DSL + server-side brackets)
+ ## Hyperliquid gotchas
- Every executed position gets THREE layers of exit, all set at entry:
+ See `references/hyperliquid-gotchas.md` for order-placement gotchas
+ (decimals, tick size, the $10 minimum, client singletons, unified-account
+ equity). Still fully current — these are exchange facts, not bot-specific.
- 1. **DSL trailing stop** (`dsl_exit.py`, primary, re-evaluated each 60s tick):
- - **Phase 1 — loss cap:** `max_loss_pct` (current live 2.5% spot) AND
- `max_loss_roe_pct/lev` (current live 25% ROE, whichever is tighter),
- optionally widened to a volatility-scaled `atr_stop` (current live ON:
- 1.5× ATR clamped 1.0–2.5%). The wider stop was validated to stop whipsawing
- volatile movers out of trend (the EIGEN/AERO leak).
- - **Phase 2 — profit lock:** engages at `protect_pct` (current live 1.25% for new entries).
- Floor =
- `entry ± peak_range × (1 − retrace_threshold)`, ratchets one-way.
- - **`retrace_threshold` + `phase2_tiers` ladder** — give-back control.
- Current live retrace is a tight **0.10** (banks give-backs early; validated
- live — JUP banked +16%/+12% ROE), loosening via tiers at +8% (0.35) and
- +15% (0.40) so proven runners breathe. `phase2_tiers` is wired from config
- at entry and on state synthesis.
- - **Breakeven ratchet:** once peak ≥ `breakeven_trigger_pct`, floor can't drop
- below `breakeven_lock_pct` — guarantees a winner can't round-trip to flat.
- - **Hard timeout:** `hard_timeout_minutes` (30h) — a scalp/swing horizon, NOT
- a multi-week hold.
- 2. **Backup stop-loss trigger** (server-side, `place_hl_trigger_order(...,"sl")`
- at `sl_atr_mult`=1.5 ATR): fires on the exchange instantly between 60s ticks,
- and is the ONLY protection while the host sleeps or the loop is restarting.
- 3. **Take-profit scale-out** (server-side, the "we had it all and gave it back"
- fix): a reduce-only TP trigger banks `tp_scale_fraction` (0.5) of the position
- at `TP_ATR_MULT`=1 ATR past entry — **half locks at target automatically**, the
- rest rides the DSL trail. Validated live (auto-banked ADA half; runner to +66%
- ROE). HL accepts a 100%-SL + 50%-TP reduce-only bracket (150% total) fine.
+ ## MCP Server
- **Trigger hygiene:** `close_position_market` calls `cancel_open_orders_for_coin`
- after a market close to clear the stranded SL/TP bracket — otherwise stale
- reduce-only orders accumulate and reject a later reduce-only order ("reduce only
- order would increase position"). Both cleanups read the coin's own dex (xyz:CL
- orders live on xyz). A position that closes outside our path (one bracket half
- filled on the exchange, a manual close) has its leftover reduce-only orders
- cancelled by `settle_vanished_positions`, after a fresh read confirms it is flat.
- See `references/exit-engine.md`.
+ `scripts/pathiel-mcp-server.py` (stdio, 59 tools) is registered the same
+ way it always was — see `references/mcp-config.md` for the config block
+ and tool-parameter table, `references/mcp-server.md` for server structure,
+ adding tools, and the audit invariant
+ (`scripts/audit_mcp_server.py` / `skills/pathiel-agent/scripts/
+ audit_mcp_server.py` — run before and after editing the server).
## Unified Accounts
- On a Hyperliquid unified account the agent wallet signs orders while the master
- account holds funds; `resolve_user_address()` picks `HYPERLIQUID_MASTER_ADDRESS`
- first, else `HYPERLIQUID_WALLET_ADDRESS`. Equity reads come from the master.
- For agent-wallet setup and the `approveAgent` flow, see the
- `hyperliquid-agent-wallets` skill.
-
- ## User Rules
-
- - Real orders only — no simulation or dry-run; mode is `OFF` or `LIVE`.
- - Full autonomy — do not ask permission for individual trade decisions.
- - Token-cost aware — a flat market with 0 triggers = $0 AI-brain spend = correct behavior.
-
- ## Market Coverage & Scan Scope
-
- Scanner uses a **bucketed budget** (default 45 core candle fetches per scan):
- - `PATHIEL_MAX_MARKETS_HIP3` (18) HIP-3 markets by 24h volume
- - `PATHIEL_MAX_MARKETS_MOVERS` (10) crypto markets by `|24h%|` above a
- `PATHIEL_MOVERS_VOL_FLOOR_USD` ($300k) floor
- - Remainder (17) crypto markets by 24h volume
-
- This catches three regimes: high-volume majors, tokenized equities, and
- low-volume native-crypto big movers (the IO/SEI/DYDX/GRASS cohort). Without
- the movers slot, BTC/ETH/SOL dominate the volume cut and every +10% midcap
- rally goes unscanned.
-
- To force coverage of a specific coin not in the buckets:
- - Call `pathiel.agents.research.research()` directly on the symbol in-process
- (confirm it's in `get_perp_markets`/`get_assets` first), or
- - Bump `PATHIEL_MAX_MARKETS_MOVERS` if it's a momentum candidate.
+ On a Hyperliquid unified account the agent wallet signs orders while the
+ master account holds funds; `resolve_user_address()` picks
+ `HYPERLIQUID_MASTER_ADDRESS` first, else `HYPERLIQUID_WALLET_ADDRESS`.
+ Equity reads come from the master. For agent-wallet setup and the
+ `approveAgent` flow, see the `hyperliquid-agent-wallets` skill.
## HIP-3 Tokenized Equity / Commodity Perps
HIP-3 markets are namespaced as `<dex>:<symbol>` and live on separate
clearinghouses (`xyz`, `km`, `vntl`, `flx`, `hyna`, `abcd`, `cash`, `para`).
- `enable_hip3` requires a loop restart because the universe is loaded once at
- startup. Dashboard/status reads use `fetch_account_state(..., include_hip3=True)`
- for aggregated equity; the executor still checks the target dex before placing a
- HIP-3 order and refuses underfunded dexes. See
- `references/hip3-tokenized-equity-handoff.md` for the exact five threaded entry
- points, `queried_dexes` DSL safety, and SDK gotchas.
-
- **Pitfall:** assuming "we scanned everything" when the log says "50 markets".
- Check MCP market-list tools when the user mentions an asset that was not reported.
-
- ## Funding-Regime Bias
-
- `risk_gates.py::market_regime_gate` applies the funding-regime overlay
- symmetrically: trades aligned with the current funding crowd use the normal bar,
- while trades against it face the elevated `counter_regime_min_conf` / composite
- bar. With current live settings, broad binary-trigger force paths stay blocked
- for counter-regime trades. Do not solve regime bias by raising
- `min_ai_confidence` globally.
-
- When the user asks "regime?", "short or long?", or similar, answer from a fresh
- `market_get_funding_regime` call. Do not rely on session memory. See
- `references/short-regime-bias.md` for code locations, exact thresholds, prompt
- template, and pitfalls.
-
- ### Testing the regime gate
-
- `tests/test_cleanup.py` has the regime + funding-regime overlay test suite
- (`test_market_regime_gate_*` and `test_funding_regime_*`). **Every test
- that touches `market_regime_gate` MUST mock BOTH the trend regime and the
- funding regime**, or the live API call from inside the gate will hit
- production and randomize the result:
-
- ```python
- from pathiel.agents import market_regime, hyperfeed
- monkeypatch.setattr(market_regime, "detect_regime", lambda c: "up")
- monkeypatch.setattr(hyperfeed, "market_get_funding_regime",
- lambda: {"regime": "NEUTRAL", "assets": []})
- ```
-
- For cache-behavior tests, also monkeypatch `_funding_regime_cache` to
- `None` and patch `_compute_funding_regime` (not `market_get_funding_regime`
- itself — that's the cache wrapper).
-
- ## Common Pitfalls
-
- | Issue | Fix |
- |-------|-----|
- | Dashboard equity ≠ HL UI total | The dashboard reads aggregated (`fetch_account_state(include_hip3=True)`). If the loop is running old code that uses main-only, restart it. |
- | Daily PnL inflated after a deposit/transfer | Contribution-aware tracking subtracts spot↔perp transfers + external deposits/withdrawals automatically. If still inflated, baseline may be stale — reset with the snippet in `references/restart-sequence.md`. |
- | Executor blocks LONG with "insufficient_free_margin" while HL UI shows plenty | `available` is `accountValue - totalMarginUsed` (matches HL UI). If they differ, the loop is on stale code — restart. |
- | Logs show overlapping scan cycles or doubled cadence | There is likely an orphan loop. Run `scripts/restart.sh status` and `ps ax \| rg "scripts/trading_loop.py"`; keep exactly one Python loop process. |
- | Most blocked LONGs are "counter-regime" | Regime proxy is slow; raise `counter_regime_min_conf` floor or rely on the own-coin-momentum bypass (composite_score≥50 or momentumBurst). |
- | MCP `config` tool dropping a key | FIXED 2026-06-05 and pruned later — the tool exposes the current risk-knob set in snake_case. Removed experiment knobs are intentionally absent. Older builds took a narrow camelCase schema, silently dropped keys, and wrote dup keys; if you see that, the MCP is on stale code → `pkill -f pathiel-mcp-server.py`. |
- | CLI brain returns PASS for every coin | Check whether the research event has `ai_down`/empty reasoning or logs show CLI timeout/non-zero exit. Provider failures intentionally return `""`; fix CLI auth/env or switch `AI_BRAIN_PROVIDER=openrouter`. Do not loosen TA sidestep to compensate. |
- | `[watchdog] no progress for N s — HUNG` re-execs (N = minutes/hours) | NOT a code hang — the host (MacBook) idle/maintenance-slept and froze the process; the watchdog re-execs correctly on wake. Confirm with `pmset -g log \| grep -iE "Sleep\|Wake"`. Fix: `caffeinate` (now auto-launched by `restart.sh`); keep on AC for closed-lid. Positions are held by server-side brackets during sleep. |
- | `reduce only order would increase position` reject | Stranded SL/TP trigger orders from a prior closed position. `close_position_market` now auto-cancels them (`cancel_open_orders_for_coin`); if on old code, cancel manually or restart. |
- | Day PnL baseline looks stale/reset after a restart | A mid-day restart can re-baseline `startOfDayEquity` to current equity (loses the true SOD) if persisted memory loaded zeroed — would launder a pre-restart drawdown out of the kill-switch. Known issue; verify `dayStartTs` vs UTC midnight before trusting daily PnL. |
- | HIP-3 position shows "no DSL" indefinitely | `get_all_hl_mids` must be called with `include_hip3=True` so trackers receive a mid. If a HIP-3 dex query times out, trackers are preserved via `queried_dexes` until the next successful query. |
- | Coin lookup fails on HIP-3 (`XYZ:MU` → not found) | Use `_norm_coin()` in MCP handlers — only the symbol uppercases, the lowercase dex prefix stays. |
- | `@` coins as noise in scan results | Spot pairs are filtered in `perception.py`; if they appear, the filter regressed. |
- | "perception not found" on research | Send the full perception object inline, not just a `perceptionId`. |
- | Order rejected on price/size | Hyperliquid `szDecimals` ≠ `pxDecimals` — see `references/hyperliquid-gotchas.md`. |
- | MCP tool runs stale code after a fix | The server is a separate process — `pkill -f pathiel-mcp-server.py` to respawn. |
- | Scan returns 0 triggers | Often correct (quiet market). Lower minScore only to widen deliberately. |
- | Scanner fires triggers but zero executes | See `references/signal-vs-action-gap.md`. First bucket the feed by `entry_preflight`, `ta_skip`, `research`, and `execute.detail`; do not lower thresholds or re-enable removed methods without fresh EV evidence. |
-
- ## Bundled Scripts
-
- Runnable tooling shipped with this skill (`scripts/`) — all read-only, no orders
- or writes. `audit_mcp_server.py` and `feed.py` are stdlib-only; `status.py` also
- does a read-only Hyperliquid query for live equity:
-
- - `scripts/audit_mcp_server.py` — validates MCP tool wiring (`TOOLS` /
- `tool_handlers` / `_STUB_RESPONSES` consistency). Run before and after editing
- the server; parses via `ast` (no import, no execution) and exits non-zero on
- drift.
- - `scripts/status.py` — plain-text snapshot showing BOTH the cached state from
- `.agent-memory.json` and LIVE state pulled directly from Hyperliquid. The
- live read uses the repo's `hl_client` and `.env.local` for credentials, so
- drift between the two surfaces a broken loop heartbeat immediately. Falls
- back to cache-only when no wallet env var is set.
- - `scripts/feed.py` — human-readable activity feed from the session log. The
- trading loop appends every scan / heartbeat / preflight / skip / research /
- execute / error event; `feed.py` renders them with timestamps and symbols.
- Examples:
- ```bash
- python3 scripts/feed.py # last 20 events
- python3 scripts/feed.py -n 50 # last 50
- python3 scripts/feed.py --since 30m # last 30 minutes
- python3 scripts/feed.py --follow # tail -f forever
- python3 scripts/feed.py --filter execute,error # only those types
- ```
- Event types emitted by the loop: `loop_start`, `loop_stop`, `loop_heartbeat`
- (per-cycle equity/positions sync), `scan` (trigger count + coins),
- `entry_preflight` (deterministic live gate skipped paid AI), `ta_skip`
- (TA filter, held/cooldown/research throttle, or pre-research runner skip),
- `research` (verdict + confidence + `ai_brain_provider` when present),
- `execute` (order outcome), `error`. `status.py` also prints TA verdict counts
- (CONFIRMED / WEAK / REJECTED + avg composite score over the last 30) so you
- immediately see if the statistical gate is over-filtering early signals.
-
- ## Visibility / "What is the bot doing right now?"
-
- MCP tools are request/response — there is no streaming progress mid-call.
- The right way to watch the trading system is:
-
- 1. **Tail the feed:** `python3 scripts/feed.py --follow` in a terminal.
- 2. **One-off snapshot:** `python3 scripts/status.py` for cached + live state.
- 3. **Hourly auto-report:** Pathiel cron job `8a82eaa567fe` (`pathiel-status.sh`)
- runs `status.py` + `feed.py --since 60m` every hour and delivers the
- combined report to the originating chat (no LLM cost — `no_agent=true`).
- - Pause: `pathiel cron pause 8a82eaa567fe`
- - Resume: `pathiel cron resume 8a82eaa567fe`
- - Run now: `pathiel cron run 8a82eaa567fe`
-
- ## Loop Heartbeat (live equity sync)
-
- `trading_loop.py` calls `_sync_account_state()` at the top of every cycle:
-
- 1. Resolves the user address via `resolve_user_address()` (master else wallet).
- 2. Calls `fetch_account_state(user, include_hip3=True)` — aggregated equity
- across main + every HIP-3 dex; returns `queried_dexes` so DSL rehydrate
- can scope its stale check.
- 3. Fetches net USDC contributions since UTC midnight via
- `fetch_aggregate_contributions_since(user, sod_ts_ms)` — subtracts
- deposits / spot↔perp transfers from the equity diff so daily PnL only
- reflects trading gains.
- 4. Calls `memory.track_daily_pnl(equity, contributions)` +
- `memory.update_open_positions(...)`.
- 5. Calls `memory.flush()` so `.agent-memory.json` always reflects current state.
- 6. Appends a `loop_heartbeat` event with equity / available / daily_pnl /
- positions + the live config snapshot (mode, frac, lev, slots, cap,
- crypto:on/off, hip3:on/off).
-
- If `status.py` shows `cached equity: $0` while LIVE is non-zero, the
- heartbeat is broken or the loop hasn't completed one cycle yet.
-
- ## Scheduled Operation
-
- An hourly Pathiel cron job (`no_agent`, zero LLM cost) runs `status.py` and
- delivers the snapshot. It ships paused — `pathiel cron resume 8a82eaa567fe` to
- start it. See `references/cron-jobs.md`.
+ `fetch_account_state(..., include_hip3=True)` aggregates equity across
+ main + every HIP-3 dex. `place_bracket`/`close_position` check the target
+ dex's own balance before placing — a HIP-3 order on an underfunded dex is
+ refused, since agent wallets cannot transfer between dexes automatically.
## References
- `references/mcp-config.md` — MCP server config and tool list.
- `references/mcp-server.md` — server structure, adding tools, the audit invariant.
- - `../../docs/AI_BRAIN_OPERATOR_WIRING.md` — Codex/Claude/Pathiel/OpenClaw brain-provider and MCP-operator wiring.
- `references/hyperliquid-gotchas.md` — order-placement gotchas (decimals, tick size, $10 min, singletons).
- - `references/cron-jobs.md` — Pathiel cron wiring for the hourly status report.
- - `references/signal-vs-action-gap.md` — current gate-first diagnostic flow for "scanner fires, trader stays silent".
- - `references/restart-sequence.md` — `scripts/restart.sh` usage + baseline-reset snippet.
- - `references/trading-mode.md` — execute-first reporting contract when the user is in active trading mode.
- - `references/daemon-investigation.md` — historical note on the no-op `--daemon` flag; superseded by `restart.sh`.
- - `references/hip3-tokenized-equity-handoff.md` — current HIP-3 production wiring (all 5 entry points, queried_dexes safety, sizing semantics).
- - `references/exit-engine.md` — DSL trail + tighter retrace ladder + breakeven, server-side SL/TP brackets, take-profit scale-out, trigger hygiene (the 2026-06-04/05 round-trip-fix overhaul).
- - `references/short-regime-bias.md` — regime-aware bias, counter-trend gating, and symmetric up/down trend surfacing.
+ - `../../docs/overhaul/MCP.md` — the generated 59-tool table with safety annotations.
+ - `../../docs/overhaul/HEADLESS_2026-10-23.md` — the full headless-desk retirement history (wave 1: the strategy books; wave 2: the autonomous engine itself).
+ - `../oil-tick/SKILL.md` — the 15-minute loop Claude actually runs to trade the oil desk.