Skip to content

feat: DEX/LP dashboard candles + symbols, and uniform bot-mode agent attribution - #169

Open
fengtality wants to merge 5 commits into
mainfrom
feat/dashboard-dex-candles-symbols
Open

feat: DEX/LP dashboard candles + symbols, and uniform bot-mode agent attribution#169
fengtality wants to merge 5 commits into
mainfrom
feat/dashboard-dex-candles-symbols

Conversation

@fengtality

@fengtality fengtality commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Problem

LP/DEX executors (connector solana-mainnet-beta, trading_pair = <mint>-SOL) had two dashboard gaps:

  • No candles — hummingbot's CandlesFactory is CEX-only, so their charts showed "Failed to load candles" (502).
  • Raw mint addresses shown as the pair instead of tickers (e.g. DezXAZ8z7…263-SOL instead of Bonk-SOL).

Additionally, bot-mode agent sessions rendered empty (no executors, flat $0 PnL) whenever the strategy derives its bot name at runtime (e.g. pmm_mister's {base}-mm) — the session never recorded which bot it operated, so nothing could attribute the bot's PnL back to it.

Changes

DEX candles + symbols (all via GeckoTerminal — same source as the pools feature + Telegram LP report)

  • Pool candlesGET /market/candles detects a DEX-network connector (or explicit pool_address) and fetches OHLCV from GeckoTerminal instead of the CEX feed. Tries the executor's own pool_address, then falls back to the base token's top live pool resolved from the mint. Reuses handlers.dex.pool_data.fetch_ohlcv + cache.
  • Time-window correctnessfetch_ohlcv gained limit / before_timestamp (threaded from the chart's requested window), so archived executors chart against the candles they actually traded in, not the latest 100. Verified against GeckoTerminal's API.
  • No negative-result cache poisoning — a transient GeckoTerminal failure is no longer cached as "" for 1h/24h; only successful responses cache.
  • GET /market/token-symbol — server-independent mint→ticker resolver (24h cache).
  • PairLabel — renders a raw base-mint pair as <SYMBOL>-<QUOTE> across executor table + detail, session view, archived charts, and the market strip. CEX pairs render unchanged.

Bot-mode attribution (6fbe36f)

  • ACP rawInput fix (latent, serious) — the claude-agent-acp adapter sends tool arguments as ACP wire field rawInput; condor read input, so on the claude-code path every tool call arrived argument-less. This silently disabled the risk engine's deploy loss-cap check, dry-run action blocking, and controller_id validation, and left snapshots without Input blocks. Now normalized at the client boundary (streamed events + permission requests).
  • Auto-capture deployed bot_nameTickEngine watches each tick's tool calls for a successful manage_bots(action="deploy") and persists the bot name into the session's config.yml (the same place static-named strategies keep it), preserving the file's mtime (it doubles as the session-start epoch for PnL window tiling). Works for every strategy with zero strategy.md changes; executor-mode strategies are untouched.
  • Closed sessions attribute too — the session-executors endpoint now merges a closed session's own time-window slice of the bot's history (same tiling as the strategy rollup) instead of returning an empty page; the response model carries bot_name + per-controller breakdown (previously computed then dropped).

Review hardening (421df77)

  • OHLCV cache eviction — the cache key now includes limit/before_timestamp (rotates by the minute for live charts), which turned the web route's process-lifetime cache dict into unbounded growth. fetch_ohlcv now sweeps expired entries on every cache miss (same contract cached_call already has).
  • Param validationmint (base58) and pool_address (base58/0x-hex) are rejected with 400 before being interpolated into GeckoTerminal URLs.

Notes

  • Backend candle path verified end-to-end (BONK mint fallback → 100 candles; before_timestamp window confirmed). Bot attribution verified live: pmm_mister session 4 now reports its bot's total −$13.89 / $45.8k volume through the real endpoint.
  • Known residual gap: a bot instance evicted from the controller-performance tables whose archived sqlite the hummingbot-api analyzer 500s on (upstream cumsum is not supported for object dtype) can't be attributed; affected sessions honestly show zero. Historical journal metrics lines are immutable — past flat-$0 timelines stay, new ticks record real values.
  • No new dependencies.

🤖 Generated with Claude Code

https://claude.ai/code/session_01T9Vygff6wPFpmZb5gahuW4

fengtality and others added 3 commits July 24, 2026 19:33
LP/DEX executors (connector solana-mainnet-beta, trading_pair <mint>-SOL) had
no candle feed — hummingbot's CandlesFactory is CEX-only, so charts showed
"Failed to load candles" — and rendered raw base-mint addresses instead of
tickers. This adds, all via GeckoTerminal (the same source the pools feature
and Telegram LP report already use):

- Pool candles: /market/candles now detects a DEX-network connector (or an
  explicit pool_address) and fetches OHLCV from GeckoTerminal instead of the
  CEX feed. It tries the executor's own pool_address, then falls back to the
  base token's top live pool resolved from the mint — so a live token always
  charts even when the passed pool is stale/absent. Reuses
  handlers.dex.pool_data.fetch_ohlcv and its cache.
- /market/token-symbol: server-independent mint -> ticker resolver
  (GeckoTerminal token info, cached 24h).
- PairLabel: renders a raw base-mint pair as <SYMBOL>-<QUOTE>, applied across
  the executor table + detail, agent session view (group header, chart title,
  positions), archived performance chart, and the agent market strip. Non-mint
  (CEX) pairs render unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9Vygff6wPFpmZb5gahuW4
…ndow

Adversarial-review follow-ups on the pool-candle path:

1. DEX candles ignored the requested window. fetch_ohlcv hardcoded
   limit=100 with no before_timestamp, so archived executors charted
   against the *latest* candles instead of the ones they traded in —
   overlays landed on an unrelated time axis. Thread limit +
   before_timestamp through fetch_ohlcv → _fetch_pool_candles_raw →
   _get_pool_candles, and pass the chart's window (bucketed_end) from
   the route. Verified against GeckoTerminal: before_timestamp walks
   history back to that window. limit is capped at GeckoTerminal's 1000.
   before_timestamp/limit are now part of fetch_ohlcv's cache key so a
   historical and a live window for the same pool don't collide.

2. Transient failures were cached as negative results. A single blip
   in _resolve_token_top_pool / get_token_symbol cached "" for 1h / 24h,
   blanking a resolvable pool/ticker long after the API recovered. Only
   cache on a successful API response (empty included — a genuinely
   unknown mint is worth remembering); an exception now returns uncached
   so the next render retries.

3. ArchivedPerformanceCharts now passes the executor's pool_address, so
   archived DEX charts use the exact pool the position traded in rather
   than the token's current top pool.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9Vygff6wPFpmZb5gahuW4
…t_name

Bot-mode sessions whose bot name is derived at runtime (e.g. pmm_mister's
{base}-mm) never persisted it, so every consumer keyed on the session's
bot_name — CORE DATA totals, the dashboard session executors/PnL, the
metrics timeline, the strategy performance rollup — rendered an empty
session while the bot traded. Only strategies with a static name in
default_config (hip_3_delta_neutral_funding_mm) attributed correctly.

Root cause found on the way: the claude-agent-acp adapter sends tool
arguments as ACP wire field `rawInput`, while condor read `input` — so on
the claude-code path EVERY tool call arrived argument-less. That silently
disabled the risk engine's deploy-cap check, dry-run action blocking, and
controller_id validation, and left snapshots without Input blocks.

- condor/acp/client.py: normalize rawInput -> input at the boundary, for
  both streamed tool_call/tool_call_update events and permission requests
  (fold input from updates too). PydanticAI path already sent `input`.
- condor/agents/engine.py: _capture_bot_name — watch the tick's tool calls
  for a successful manage_bots(action="deploy") and persist the deployed
  bot_name into the session's config.yml (same place the delta-neutral
  strategy keeps its static name), preserving the file's mtime, which
  doubles as the session-start epoch for history window tiling. Works for
  every strategy with zero strategy.md changes; executor-mode strategies
  (lp_slot_operator) never deploy so nothing changes for them.
- condor/web/routes/agents.py: session-executors endpoint now resolves the
  bot for non-operator (closed) sessions too, attributing that session's
  time-window slice of the bot's history (same tiling as
  _apply_bot_mode_pnl) instead of returning an empty page; response model
  now carries bot_name + per-controller breakdown (previously computed and
  dropped).
- frontend/src/lib/api.ts: AgentPerformance gains optional bot_name /
  controllers.

Known residual gap: a bot instance that vanished from the
controller-performance tables AND whose archived sqlite the archived-bots
analyzer can't parse (upstream "cumsum is not supported for object dtype"
500) cannot be attributed; its sessions honestly show zero.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9Vygff6wPFpmZb5gahuW4
@fengtality fengtality changed the title feat(dashboard): DEX/LP pool candles + token-symbol resolution feat: DEX/LP dashboard candles + symbols, and uniform bot-mode agent attribution Jul 25, 2026
fengtality and others added 2 commits July 25, 2026 06:35
…address params

The OHLCV cache key now includes limit/before_timestamp, which rotates by
the minute for live charts — with no eviction on the get/set path the web
route's process-lifetime dict grew without bound (~0.3MB per entry). Sweep
expired entries on every cache miss, same contract cached_call already has.

Also reject a mint (base58) or pool_address (base58/0x-hex) that doesn't
look like an address before interpolating it into a GeckoTerminal URL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9Vygff6wPFpmZb5gahuW4
@rapcmia rapcmia moved this to Condor in Pull Request Board Jul 28, 2026
@rapcmia rapcmia self-assigned this Jul 28, 2026
@fengtality
fengtality requested a review from cardosofede July 28, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Condor

Development

Successfully merging this pull request may close these issues.

2 participants