Skip to content

feat(portfolio): add cross-exchange portfolio summary#164

Merged
ChoKhoOu merged 1 commit intomainfrom
feat/cross-exchange-portfolio
Feb 25, 2026
Merged

feat(portfolio): add cross-exchange portfolio summary#164
ChoKhoOu merged 1 commit intomainfrom
feat/cross-exchange-portfolio

Conversation

@ChoKhoOu
Copy link
Copy Markdown
Owner

Summary

  • Add cross_exchange_summary and cross_exchange_positions actions to the portfolio tool
  • Aggregate balances, positions, and PnL across all connected CEX exchanges (Binance, Bybit, OKX, Bitget)
  • Add /portfolio slash command for quick cross-exchange portfolio view
  • Update tool descriptions for AI agent discoverability

Closes #47

Test Plan

  • bun run typecheck passes
  • bun test passes
  • Cross-exchange summary action returns aggregated balances from all exchanges
  • Cross-exchange positions action returns merged positions with exchange labels
  • Asset distribution percentages are correctly calculated
  • /portfolio slash command triggers cross_exchange_summary action

🤖 Generated with Claude Code

… balances and positions

- Add getAccountBalance/getExchangePositions methods to ExchangeClient
- Add cross_exchange_summary action: aggregates balances across all CEX
  exchanges (Binance, Bybit, OKX, Bitget) with asset totals and distribution
- Add cross_exchange_positions action: aggregates open positions across
  exchanges with total unrealized PnL
- Add /portfolio slash command
- Update portfolio tool description with new actions
- Add tests for both cross-exchange actions including error handling

Closes #47
@github-actions github-actions bot added type/tests Test-related changes area/cli CLI and TUI (Ink) area/grpc gRPC / ConnectRPC layer area/tools Agent tools labels Feb 25, 2026
Copy link
Copy Markdown
Owner Author

@ChoKhoOu ChoKhoOu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

COMPLETE

Requirement Verification: PR #164 vs Issue #47 (PRD-045)

All 6 acceptance criteria from the original issue are fully implemented:

# Acceptance Criterion Status Evidence
1 Aggregate balances across exchanges PASS cross_exchange_summary action in portfolio.tool.ts queries all 4 exchanges (binance, bybit, okx, bitget) via Promise.allSettled, returns exchangeBalances + aggregatedAssets. Test verifies multi-exchange aggregation.
2 Aggregate positions across exchanges PASS cross_exchange_positions action merges positions from all exchanges with exchange labels. Test verifies positions from binance + bybit are correctly combined.
3 Total PnL correctly calculated PASS totalUnrealizedPnl sums unrealizedPnl across all exchange positions. Test verifies: 500 + (-100) = 400.
4 Asset distribution percentages PASS distribution array in handleCrossExchangeSummary calculates per-exchange percentage based on USDT value. Test verifies binance at 63.16% (6000/9500).
5 AI conversation queryable PASS Tool description in portfolio.ts updated with cross-exchange use cases ("Viewing aggregated balances and positions across all connected exchanges", "Checking total portfolio value and asset distribution across exchanges"). New actions documented in the action table for agent discoverability.
6 /portfolio slash command PASS Command registered in SLASH_COMMANDS registry, handler added in parseSlashCommand, action type added to SlashAction union. Tests confirm parsing returns { handled: true, action: 'portfolio' }.

Technical Requirements (from PRD)

  • Enhanced src/tools/consolidated/portfolio.tool.ts — PASS
  • New actions cross_exchange_summary and cross_exchange_positions — PASS
  • Parallel gRPC queries via Promise.allSettled — PASS
  • Graceful error handling per exchange — PASS (tested with mixed success/failure scenarios)

Test Coverage

  • cross_exchange_summary: aggregation test + error handling test
  • cross_exchange_positions: aggregation test + error handling test
  • /portfolio slash command: parsing test + registry count updated

All requirements from Issue #47 are fully satisfied.

Copy link
Copy Markdown
Owner Author

@ChoKhoOu ChoKhoOu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE

Code Review: feat(portfolio): add cross-exchange portfolio summary

Overall Assessment

Clean, well-structured implementation that follows existing codebase patterns. The use of Promise.allSettled for parallel exchange queries is the correct approach for graceful partial-failure handling. Test coverage is thorough with both happy-path and error scenarios.

Strengths

  • Resilient parallel queries: Promise.allSettled ensures one exchange failure doesn't block others — critical for multi-exchange workflows.
  • Consistent patterns: ctx.grpc?.exchange ?? getExchangeClient() follows the same DI-with-fallback pattern used throughout the tool layer.
  • Good test design: Tests inject mocks via ctx.grpc.exchange rather than module-level state, which is cleaner and avoids inter-test pollution.
  • Schema + description updated together: New actions are properly registered in both the Zod schema and the AI-facing description, ensuring agent discoverability.

Minor Notes (non-blocking)

  1. totalUsdtValue only sums stablecoins (portfolio.tool.ts:131-133): The field name suggests total portfolio value, but it only aggregates USDT/USDC/USD assets. Non-stablecoin assets (BTC, ETH) are included in aggregatedAssets but not in the total or distribution percentages. This is a reasonable V1 approach (proper conversion would require price feeds), but consider renaming to totalStablecoinValue or adding a comment clarifying the limitation.

  2. Hardcoded SUPPORTED_EXCHANGES array (portfolio.tool.ts:6): ['binance', 'bybit', 'okx', 'bitget'] requires code changes to add exchanges. Consider extracting to a shared constant or reading from daemon config in a future iteration.

  3. Cross-domain import (portfolio.tool.ts:4): getExchangeClient is imported from ../trading/grpc-clients.js. This creates a portfolio→trading module dependency. Acceptable since ExchangeClient is shared infrastructure, but worth noting for future refactoring.

  4. Distribution percentages can be misleading: Since totalUsdtValue only counts stablecoins, an exchange holding 10 BTC and 0 USDT would show 0% distribution despite significant value. The exchangeBalances array provides the full picture, so agents can still reason about it correctly.

Verification Checklist

  • Proto definitions for GetAccountBalance and GetExchangePositions exist in proto/tino/exchange/v1/exchange.proto
  • Generated types (GetAccountBalanceRequestSchema, GetExchangePositionsRequestSchema) are available in src/grpc/gen/
  • ExchangeClient new methods match proto service definition
  • /portfolio slash command correctly registered in SLASH_COMMANDS, SlashAction type, parser, and runExtendedSlashAction
  • Test count updated (23 → 24 commands)
  • Tool description updated for AI discoverability
  • Error handling correctly propagates exchange names in error messages
  • symbol ?? "" default aligns with protobuf string default semantics

@ChoKhoOu ChoKhoOu merged commit bd7843b into main Feb 25, 2026
3 of 6 checks passed
@ChoKhoOu ChoKhoOu deleted the feat/cross-exchange-portfolio branch February 25, 2026 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/cli CLI and TUI (Ink) area/grpc gRPC / ConnectRPC layer area/tools Agent tools type/tests Test-related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant