Execute deterministic close/claim without the LLM#91
Open
agushudono wants to merge 11 commits into
Open
Conversation
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>
59e72e7 to
7b21a32
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The management cycle decides exits deterministically in JS (
getDeterministicCloseRule+updatePnlAndCheckExits), but the actualclose_position/claim_feescall 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:CLOSE(stop-loss / trailing-TP / OOR / low-yield)executeTool("close_position")directlyCLAIM(fees ≥minClaimAmount)executeTool("claim_fees")directlyINSTRUCTION(free-text condition)The LLM is now only invoked for
INSTRUCTIONpositions, and that call is wrapped intry/catchso a provider failure no longer aborts the cycle after deterministic closes have already run.Why this is safe
executeTool(...)(not rawclosePosition), so all side-effects are preserved:recordClose/recordPerformance(insideclosePosition),notifyClose+ auto-swap base→SOL (insideexecuteTool), decision logging, pool-memory annotation.toolStart/toolFinishprogress still updates per position.runSafetyChecksforclose_position/claim_feesfalls through topass: true, so nothing is newly blocked.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