- POST /api/launch/preflight and GET /api/traders/:id/preflight run every
launch prerequisite (AI model credential, claw402 wallet key + Base USDC
balance, strategy, exchange config, live account state, funding minimums)
and return a structured checklist with stable codes; minimums (1 USDC AI
fee / 12 USDC trading) now live server-side and are exposed in the response.
- POST /traders/:id/start enforces the preflight (400 with error_key
trader.start.preflight_failed + full checklist; ?force=true to override).
Funding gates use max(available, equity) so restarting a bot with deployed
margin is not blocked; indeterminate probes (RPC outage) degrade to
warnings instead of blocking.
- Run loop now surfaces runtime health via GetStatus: safe_mode(+reason) and
ai_wallet_status/balance (ok|low|empty|unknown), including typed detection
of claw402 ErrInsufficientFunds instead of burying it in logs; safe-mode
state is mutex-guarded for API readers.
- Onboarding wallet endpoints switch to the cached, error-aware balance query
and report balance_status so the UI can tell an RPC outage from an empty
wallet.
A single-symbol QNT-USDC trader produced 0 candidate coins, 500 errors
from Hyperliquid, and "🚫 Dropped AI decision" warnings — the agent had
no market data to reason about, so it sat in `wait` forever. Three
chained bugs:
1. provider/hyperliquid/kline.go (IsXYZAsset / FormatCoinForAPI):
asset detection required the base symbol to appear in the hardcoded
StockPerpsSymbols / XYZOtherSymbols / display-alias lists. QNT, ARM,
and every other newly-listed Hyperliquid USDC perp wasn't in the
list, so the code routed them to the crypto path (CoinAnk) which
doesn't have them. Now the `-USDC` suffix and `xyz:` prefix are
trusted as definitive Hyperliquid signals — these tokens are
Hyperliquid-specific and new listings don't require a code change.
The hardcoded lists are kept as fallbacks for bare base symbols.
2. market/data_klines.go (getKlinesFromHyperliquid): the function
stripped the `xyz:` prefix before calling GetCandles, defeating
GetCandles's own FormatCoinForAPI logic. With the hardcoded list
missing the new ticker, FormatCoinForAPI couldn't re-add the prefix
and the request hit Hyperliquid's crypto perp endpoint — which
returns 500 for stock-only tickers. Pass the symbol through as-is.
3. trader/auto_trader_loop.go (filterDecisionsToStrategyUniverse): the
AI sometimes echoes a candidate as "QNTUSDC" / "QNT-USDC" / "QNTUSDT"
/ bare "QNT" instead of the canonical "xyz:QNT" we supplied. Strict
exact-match was dropping all of them. Added a base-level key
(strips xyz:, -USDC, -USDT, USDC, USDT, USD; normalizes display
aliases like ROBINHOOD → HOOD) and rewrites the matched decision's
symbol to the canonical form so the order pipeline downstream sees
the format it expects.
After this, a single-symbol stock trader fetches real K-line data from
Hyperliquid, the AI sees real candidates, and decisions get executed
on-chain instead of silently filtered.
- Track consecutive AI failures
- After 3 failures: activate safe mode (no new positions, close/hold only)
- Auto-deactivate when AI recovers
- Keep loop running in safe mode (retry each cycle)
- Log clearly: 🛡️ SAFE MODE ACTIVATED/DEACTIVATED