Honest net learning, rich close report, interactive position cards, paper sim fixes#92
Open
zenginamca00 wants to merge 185 commits into
Open
Honest net learning, rich close report, interactive position cards, paper sim fixes#92zenginamca00 wants to merge 185 commits into
zenginamca00 wants to merge 185 commits into
Conversation
- Delete hive-mind.js - Remove hive pattern consensus from management cycle prompt - Remove hive pool consensus from screening cycle - Remove syncToHive() call from lessons.js recordPerformance Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two root causes fixed: 1. Auto-swap after close was silent — model didn't know it happened and called swap_token again. Now executor appends auto_swap_note to the close_position result so the model sees the swap is done. 2. Added ONCE_PER_SESSION guard in agent loop for deploy_position, swap_token, close_position. If the model tries to call any of these twice in the same session, the second call is blocked with a clear explanation returned as the tool result. Also fixed screening TOCTOU race: _screeningBusy=true now set before any await, with proper reset on early-exit paths. Removed startCronJobs() call from inside management cycle (was causing cron restart mid-run). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove empty hivePatterns variable (hive-mind already deleted) - Replace "CLOSED, FLIPPED" with "CLOSED" (flip bid-ask removed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 5-minute cache was causing management to see stale position counts, leading to wrong display, broken maxPositions enforcement, and double deploys slipping through. - Management cycle entry: force:true - Screening cycle pre-check: force:true (the guard that enforces maxPositions) - Startup display: force:true - /status command: force:true Shutdown log keeps cache (no need for fresh data on exit). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Separate instruction logic from close rules — instructions now explicitly override ALL close rules, preventing rule 3-5 from firing on positions that have a note/instruction set - Remove progress bar from report format (saves tokens, unreliable) - Show note in report when instruction is set - Drop maxOutputTokens 4096→1024 - Add "Write ONLY the report. No preamble, no reasoning." instruction Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reasoning models (Nemotron, Qwen3, etc.) consume ~800 tokens on internal thinking before writing output. 1024 left only ~200 tokens for the actual report causing truncated responses (finish_reason: length). 2048 = ~800 reasoning + ~1200 for report, plenty for any position count. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- getMyPositions() now returns instruction and fee_per_tvl_24h directly (both were already fetched from PnL API, just not exposed) - Management cycle no longer calls getPositionPnl() per position — that was a redundant second API hit for data already in getMyPositions - instruction was never reaching the LLM (field missing from return object) — now positions with notes are correctly shown to the model - Removes getPositionPnl import from index.js (no longer used there) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…io API Old flow: getProgramAccounts (raw RPC byte parsing) + fetchDlmmPnlForPool per unique pool = N+1 calls minimum. New flow: single GET /portfolio/open?user=<wallet> returns all positions with full financial data (balances, pnl, fees, feePerTvl24h, OOR status). Only fetches per-pool PnL for OOR positions (to get active_bin for close rules). In-range positions use tracked state for lower/upper bin. Also fixes: pair name now uses tokenX/tokenY symbols from API when pool_name not tracked. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strip positions (pre-loaded in goal), memory, performance, screening config, schedule config, timeframe table, and pretty-print whitespace. Keep portfolio (compact), management config (compact), and lessons. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously, screening was launched before agentLoop ran, so if the manager decided to close a position, the screener would already be running with a stale position count and race to deploy simultaneously. Now screening is triggered after agentLoop returns, using a fresh position count — ensuring the screener sees the actual post-close state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add "no extended analysis" hint to MANAGER system prompt — reasoning models were burning ~1969/2048 tokens thinking before writing the report. Bump management maxOutputTokens 2048→4096 so output isn't truncated even if the model still reasons ~1000 tokens. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
deploy_position blocked by safety checks (duplicate token, max positions) was marking the slot as used, preventing any further deploy attempts in the same screening cycle. Now only marks as fired if the tool actually executed (result.blocked is falsy). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… prompts 1. Drop getTokenHolders — getTokenInfo already has Jupiter audit (top10%, bots%, fees). Removes 2 API calls per candidate x5 = 10 fewer per cycle. 2. JS hard-filter (fees/top10/bots/launchpad) before LLM — 0 LLM tokens if no candidates pass. 3. Fully parallel recon fetch for all candidates simultaneously. 4. Pre-fetch active_bin for passing candidates — removes get_active_bin tool-call step from every cycle. 5. Slim SCREENER system prompt: ~250 tokens vs ~3000+ before. 6. Narrative capped at 500 chars (the only real LLM judgment call). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- config: trailingTakeProfit (default on), trailingTriggerPct=3%, trailingDropPct=1.5% - state: track peak_pnl_pct + trailing_active per position - state: updatePnlAndCheckExits() — pure JS, updates peak, activates trailing at trigger%, fires TRAILING_TP when drop from peak >= dropPct - index: run exit checks before agentLoop, inject HARD EXIT ALERTS into management prompt — LLM executes the close, JS made the decision - executor: register new config keys in update_config mapping Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lightweight setInterval that fetches positions every 30s and runs updatePnlAndCheckExits to keep peak_pnl_pct/trailing_active current. No LLM involved — pure state update. Skips if management/screening busy. Exit alerts logged immediately; management cycle acts on them next run. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Sequential recon fetch with 150ms delay between candidates (was fully parallel — 15 concurrent requests causing 429s) - close_position gains optional reason param; LLM instructed to always pass it - After low-yield close, addPoolNote so screener sees it next cycle and avoids redeploying to the same pool Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add silent param to getMyPositions — poller uses silent:true to avoid spamming [POSITIONS] logs every 30 seconds. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove JS hard filter (fees/top10/bots). LLM judges everything itself with pre-loaded audit, narrative, smart wallets, active_bin. Only blocked launchpads are still filtered in JS. More pool diversity without extra LLM tool calls. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…o module exports
- Copy cli.js from main branch (JSON stdout, SKILL.md, agent-native commands)
- Hoist runManagementCycle and runScreeningCycle to module-level exports
- Both accept { silent } param to suppress Telegram from CLI invocations
- startCronJobs now delegates to the exported functions
- Add bin entry to package.json for `meridian` CLI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ts, narrative meridian candidates now preloads everything like the screening cycle: - Token audit (top10%, bots%, global_fees_sol, launchpad) - Full holder list with bundler detection - Smart wallets in pool - Token narrative - Active bin - Pool memory recall Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously a failed deploy_position still marked the session as fired, blocking any retry. Now only result.success === true triggers the lock. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strip existing `import BN from "bn.js"` lines before prepending, so repeated runs don't accumulate duplicate declarations. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 5 close rules + claim rule now evaluated in JS deterministically. Report is built in JS every cycle without LLM. LLM only invoked when a close/claim/instruction action is needed. - Reduces LLM cost and latency on every idle cycle - Positions with instruction still passed to LLM for natural language eval - Exit alerts (trailing TP) still trigger immediate LLM close Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If finalValueUsd is 0 but a SOL deposit exists, wait 5s and re-fetch from the PnL API before recording performance/lesson. Prevents -99% false FAILED lessons caused by the portfolio API not yet settling. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use total_value_usd/unclaimed_fees_usd in report (sol_value field doesn't exist) - Skip PnL close rules if pnl_pct < -90% but position still has value (bad API data) - Fix summary currency symbol for solMode Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Positions deployed before initial_value_usd tracking was added had null in state.json, causing pnl_pct=0% and pnl_usd=finalValue in pool-memory. Now falls back to (finalValueUsd - pnlUsd) which correctly reconstructs the initial deposit value from the API's reported PnL. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace cache snapshot + 5s wait + status=open re-fetch with direct status=closed API call after position is withdrawn. The closed API returns authoritative allTimeDeposits, allTimeWithdrawals, allTimeFees, pnlUsd, and pnlPctChange — no settlement lag, no guessing initial value. Falls back to pre-close cache snapshot only if the closed API returns no data for the position. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three related fixes: - numeric(null) now returns null instead of 0 (Number(null)===0 was causing pools with null volume to be rejected as "volume 0") - applyVolatilityTimeframe now also fetches and promotes volume from the longer timeframe (30m floor) when primary window is too short; both windows exposed to LLM as volume_5m/volume_30m and volatility_5m/volatility_30m - Discord-only signal pools now get a fresh fetchPoolDiscoveryDetail call before filtering — their discovery_pool is a stale snapshot so volume/ fee/volatility can be 0 even when the pool is currently active Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New simulate_lp_position tool in tools/simulator.js: - Replays real 5m OHLCV candles to estimate fee earnings and IL - Supports Spot (uniform), Curve (bell/center-weighted), and Bid-Ask (edge-weighted) liquidity distribution strategies - Computes per-bin TVL share via DLMM SDK (falls back to pool TVL estimate if RPC unavailable) - Outputs: feesEarned, ilUsd, ilPct, netPnL, inRangePct, tvlShareAvg, annualizedFeeApr - Wired into definitions.js, executor.js, and both MANAGER/SCREENER tool sets in agent.js Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces one-shot historical replay with a live forward simulation. Paper positions open now and accumulate real market data every 5m. paper-positions.js (new): - openPaperPosition: computes initial X/Y split via sqrt-price geometry, stores entry state to paper-positions.json - tickPaperPositions: fetches OHLCV candles from last_candle_timestamp → now, accrues fees (volume × lp_fee_rate × tvl_share, only when price in range) and recomputes IL (current token value vs hodl value) per candle - getPaperPosition / closePaperPosition / listPaperPositions tools/simulator.js: thin wrappers over paper-positions.js index.js: dedicated */5 * * * * cron to tick all open paper positions tools/definitions.js: 4 tools — open/get/close/list_paper_position tools/executor.js + agent.js: wired into MANAGER + SCREENER tool sets Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Detect price scale factor between SDK bin prices and datapi/OHLCV scale at open time (token decimal differences cause ~1000x discrepancy) - Store normalized lower/upper/entry prices consistent with OHLCV candle scale - Fix IL formula: replace broken USD-fraction rebalance (which always summed to depositAmount) with correct price ratio formula: IL = deposit × (2√r/(1+r) - 1) × √(upperPrice/lowerPrice) where r = effectivePrice/entryPrice (clamped to range) - openPaperPosition now returns formatSummary instead of raw state object Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- dlmm.js: include lower/upper price in dry-run would_deploy - index.js: bridge dry-run deploy -> openPaperPosition (max positions + dedup), auto-close via evaluatePaperExits in paper-sim cron - paper-positions.js: evaluatePaperExits (SL/TP/trailing/OOR), peak+OOR tracking, pool_address in summary - telegram.js: message_thread_id (topic) support - config.js + agent.js: OpenRouter provider routing - paper-stats.mjs: Charonica-style validation gate report Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ions The bridge was only in runScreeningCycle's hook, so 'auto'/manual '1/2/3' deploys did not open paper positions. Moved to the central deploy_position success hook in executor.js (awaits the async list_paper_positions wrapper). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…p banner DEFAULT_MODEL fell back to openrouter/healer-alpha (retired) when no model was passed, breaking 'auto'/manual deploy/chat in the REPL. Fall back to config.llm.generalModel instead. Also fix the cosmetic startup banner. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…age) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Close paper positions stuck in limbo (open >= staleCloseMinutes with in-range < staleMaxInRangePct) so unproductive positions free their slot and turnover improves. Defaults: 120m age, 25% in-range. paper-stats shows a STALE exit category. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- screening: requireSolQuote (default true) rejects non-SOL-quote pools (fixes GACHA-USDC getting picked while agent deploys single-side SOL) - paper bridge: dedup by base_mint too, not just pool_address (no more GACHA-SOL + GACHA-USDC held simultaneously) - getTopCandidates returns total_eligible (fixes 'undefined eligible' display) - thread base_mint through would_deploy -> openPaperPosition -> summary Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- strategy default: bid_ask → curve - bins_above = bins_below in screener prompt (center range at active bin) - Remove executor guard blocking bins_above > 0 for single-sided deploys - Allow curve in definitions enum; drop "Never use curve" hard rule Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…aper sim fixes Learning signal (lessons.js + config.js): - recordPerformance now saves gross_pnl_usd (Meteora, pre-cost) and lesson_id so the lesson can be re-derived once realized costs arrive - applyRealizedCosts(): subtract actual gas + swap slippage from perf record and re-derive lesson from net; idempotent via costs_applied flag - evolveThresholds: fix dead key maxVolatility (doesn't exist in config) and minFeeTvlRatio → minFeeActiveTvlRatio (correct key); gate behind autoEvolveEnabled (new config.management key, default false); fire notifyEvolve Telegram alert on every change — never evolves silently - config.js: add autoEvolveEnabled to management section Rich close report (telegram.js + executor.js + dlmm.js): - closePosition() return object enriched with fees_usd, minutes_held, minutes_oor, num_txs, close_reason, meta_vol, meta_bin_step, meta_fee_tvl - finalizeClose() exported helper: swap FIRST (realized slip), then compute honest net = gross − gas − slip, calls applyRealizedCosts + notifyClose - notifyClose() rewritten: Gross / fees / Gas / Swap slip / NET / Exit / Pool meta breakdown; hasActiveLiveMessage() guard removed so autonomous closes always surface - notifyEvolve() added: threshold-change alert with per-key rationale - All three manual close paths in index.js (/close, /closeall, close button) now call finalizeClose so the report and swap are consistent with the agent loop Interactive position cards (telegram.js + executor.js + index.js): - sendPositionCard / updatePositionCard / getPositionCard / removePositionCard - Inline keyboard: Close, Refresh, TP 3/5/10%, SL 10/20/30%, Trail toggle - handlePositionCallback() routes pos: callbacks for both paper and live positions - notifyDeploy() replaced with sendPositionCard on deploy Paper sim fixes (paper-positions.js + paper-stats.mjs + wallet.js): - Filter out currently-forming candle so fees never show $0 permanently - Pass deposit_sol through so paper-stats can display ◎ amounts - paper-stats GATE_WR 40→15% (realistic floor for LP) - applyPaperWalletSol(): in DRY_RUN, override getWalletBalances with PAPER_WALLET_SOL env so agent doesn't read 0 SOL and skip all entries - Topic filter for incoming Telegram messages (only accept configured thread) Screener prompt (prompt.js): - Single-candidate rule: allow deploy when metrics strong, even 0 smart wallets - Entry timing heuristic: prefer fee_change_pct>0/volume_change_pct>0 pools; avoid deploying into fading post-peak activity Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Summary
applyRealizedCosts()subtracts realized gas + swap slippage from performance records after close; lessons now derived from net (not Meteora gross).evolveThresholdsfixed (deadmaxVolatilityblock removed,minFeeTvlRatio→minFeeActiveTvlRatio), gated behindautoEvolveEnabledconfig key, firesnotifyEvolveTelegram alert on every change.finalizeClose()shared helper — swap FIRST (realized slip), then send breakdown: Gross / fees earned / Gas (est) / Swap slippage (realized) / NET (real). RemovedhasActiveLiveMessage()guard so autonomous closes always surface. All three manual close paths (/close,/closeall, close button) now go throughfinalizeClose.sendPositionCardreplaces plainnotifyDeploy; inline keyboard with Close, Refresh, TP 3/5/10%, SL 10/20/30%, Trail toggle.handlePositionCallbackinindex.jshandles both paper and live positions.deposit_solthrough for ◎ display in paper-stats;GATE_WR40→15%;applyPaperWalletSol()overridesgetWalletBalancesin DRY_RUN so agent doesn't read 0 SOL and skip all entries.Test plan
/close, close button, or agent autonomous close → rich breakdown notification fires (Gross / Gas / Slip / NET)autoEvolveEnabled=true→notifyEvolvefires if thresholds change; no silent evolutionpaper-stats.mjs; verify current-forming candle is excluded from fee calcPAPER_WALLET_SOLin.env→ screener reads the override balance, not 0🤖 Generated with Claude Code