Skip to content

Execute deterministic close/claim without the LLM#91

Open
agushudono wants to merge 11 commits into
yunus-0x:mainfrom
agushudono:fix/deterministic-close-without-llm
Open

Execute deterministic close/claim without the LLM#91
agushudono wants to merge 11 commits into
yunus-0x:mainfrom
agushudono:fix/deterministic-close-without-llm

Conversation

@agushudono

Copy link
Copy Markdown

Problem

The management cycle decides exits deterministically in JS (getDeterministicCloseRule + updatePnlAndCheckExits), but the actual close_position/claim_fees call was delegated to the LLM. When the LLM provider was unavailable (in production: "400 Your credit balance is too low"), every management cycle failed at step 0 — so stop-loss, trailing-TP, OOR, and low-yield exits never executed.

Observed impact: a position sat at its stop-loss threshold and bled to -33% for ~6 hours because no close could fire while the provider was down. Fee claims were also blocked.

Fix

runManagementCycle() now executes the deterministic actions directly:

Action Before After
CLOSE (stop-loss / trailing-TP / OOR / low-yield) LLM calls the tool JS calls executeTool("close_position") directly
CLAIM (fees ≥ minClaimAmount) LLM calls the tool JS calls executeTool("claim_fees") directly
INSTRUCTION (free-text condition) LLM LLM (unchanged — can't be parsed in JS)

The LLM is now only invoked for INSTRUCTION positions, and that call is wrapped in try/catch so a provider failure no longer aborts the cycle after deterministic closes have already run.

Why this is safe

  • Goes through executeTool(...) (not raw closePosition), so all side-effects are preserved: recordClose/recordPerformance (inside closePosition), notifyClose + auto-swap base→SOL (inside executeTool), decision logging, pool-memory annotation.
  • Live Telegram toolStart/toolFinish progress still updates per position.
  • runSafetyChecks for close_position/claim_fees falls through to pass: true, so nothing is newly blocked.
  • Standalone notifications remain suppressed while a live message is active (existing behavior).

Net result: stop-loss / trailing-TP / OOR / low-yield / claim now work regardless of LLM availability. The LLM is value-add for manual instructions, no longer a single point of failure for safety.

🤖 Generated with Claude Code

root and others added 11 commits May 30, 2026 02:52
AbortError from fetchWithTimeout has no HTTP status, so isRetryableStatus(0)
was false and a single 10s per-attempt timeout threw immediately — never
using the 30s maxElapsedMs retry budget. Treat timeouts/aborts as retryable
so transient relay slowness retries before falling back to direct LPAgent fetch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FALLBACK_MODEL was "stepfun/step-3.5-flash:free", an OpenRouter model name
that 404s against the configured Anthropic endpoint — so a transient 502/503/529
on the primary model would switch to a fallback that itself errors. Point it at
claude-haiku-4-5-20251001 (fast/cheap, available on this API key).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Source-level fallbacks still pointed at openrouter/minimax model
names (healer-alpha, hunter-alpha, minimax-m2.x) which 404 against
the Anthropic-compatible endpoint. Align agent.js, config.js and the
example config with the live user-config: default to claude-opus-4-7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Default model claude-opus-4-7, fallback claude-haiku-4-5-20251001,
default base URL api.anthropic.com; clarify OPENROUTER_API_KEY is a
legacy fallback (live key comes from user-config llmApiKey).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fetchMyPositions called the Agent Meridian relay (and its direct
LPAgent fallback) before the portfolio lookup, but the result is only
consumed in the per-position loop. With an empty wallet that loop never
runs, so every management/screening cycle awaited the relay for its full
~30s retry budget and logged a misleading "raw relay failed; falling
back" warning — 77x today, and on every `npm run dev`. Gate the relay +
direct fetch on pools.length > 0 so an empty wallet skips them entirely.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
closePosition: disable the relay zap-out attempt. It failed 42/42 closes
(the OKX zap-out order embeds an owner SOL transfer that the safety guard
assertNoUnsafeSystemTransfer rejects) and always fell back to the local
close + Jupiter autoswap. The doomed attempt added sign→simulate→throw
latency to every close, worsening fills during fast dumps, and requested a
50% (slippageBps 5000) swap. Go straight to the safer, working local path;
the guard logic is untouched and the relay is easily re-enabled via the flag.

deriveLpAgentPnlPct: add collectedFee to the PnL term so already-claimed
fees aren't dropped. Omitting it understated PnL by the claimed amount after
every claim_fees, producing a constant reported-vs-derived gap and false
"suspicious PnL" flags.

Risk-exit tuning (stopLossPct -10→-6, trailingTriggerPct 3→5) lives in the
gitignored user-config.json and is applied there separately.

Activates on restart; the live process is a stale 2026-06-06 in-memory snapshot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Symmetric downside handling for single-side SOL positions, which catch a
falling knife when price dumps through the entry range (the main source of
the -16/-21% tail losses).

- index.js getDeterministicCloseRule: add rule 6 (close when active bin is
  more than outOfRangeBinsToClose below the range — mirror of rule 3) and
  rule 7 (OOR-below past outOfRangeWaitMinutes — mirror of rule 4).
- index.js computeStrategy(volatility): pick LP shape by volatility —
  bid_ask at >= 4, else spot — and use it on the programmatic deploy path
  and in the screener prompt (single source of truth). Refuses non-finite/
  non-positive volatility.
- tools/definitions.js: align deploy_position strategy guidance with the
  volatility-driven rule.
- lessons.js: detect "dumped below range" closes, emit a specific
  falling-knife AVOID lesson (downsize / require momentum for that
  volatility tier), and count the dump as negative evidence.

Activates on restart; the live process is a stale 2026-06-06 snapshot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Newer Opus models deprecate the temperature parameter; the request 400s with
a "temperature ... unsupported/deprecated" error. Detect that case and retry
the same step once with temperature omitted (same pattern as the existing
thinking-mode tool_choice fallback), latching omitTemperature for the run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve diverged main (local 8 ahead / 2 behind). Conflicts in
index.js, tools/definitions.js, tools/dlmm.js, CLAUDE.md.

Strategy selection now combines both approaches:
- config.strategy.strategy === "auto" → volatility-driven shape
  (bid_ask if volatility >= 4, else spot), resolved in deployPosition
- any other value ("bid_ask"/"spot") → explicit config override
- default + example now "auto"

Also realign CLAUDE.md model docs to actual Anthropic code defaults
(claude-opus-4-7 / claude-haiku-4-5 fallback) after the remote rewrite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The management cycle decided exits deterministically in JS but relied on
the LLM to actually call close_position/claim_fees. When the LLM provider
was unavailable (out of credits), every management cycle failed at step 0,
so stop-loss / trailing-TP / OOR / low-yield exits never fired — a position
was left bleeding to -33% for hours.

Now CLOSE and CLAIM actions run directly via executeTool() in JS, so safety
rules and fee claims work regardless of LLM availability. Only INSTRUCTION
positions (free-text conditions that can't be parsed in JS) are deferred to
the LLM, and that call is wrapped in try/catch so a provider failure no
longer aborts the whole cycle after deterministic closes have run.

Going through executeTool preserves all close side-effects (recordClose/
recordPerformance in closePosition; notifyClose + auto-swap base->SOL in
executeTool), decision logging, and live Telegram tool progress.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reflect that CLOSE/CLAIM now execute directly in JS and the LLM is only
invoked for INSTRUCTION positions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant