Skip to content

feat: add portfolio performance chart with range selectors - #1068

Merged
Chucks1093 merged 1 commit into
accesslayerorg:devfrom
sksneh:cloud-fixer/1052-portfolio-performance-chart
Sep 29, 2026
Merged

Chucks1093 merged 1 commit into
accesslayerorg:devfrom
sksneh:cloud-fixer/1052-portfolio-performance-chart

Conversation

@sksneh

@sksneh sksneh commented Sep 28, 2026

Copy link
Copy Markdown

Overview

Adds a portfolio performance line chart to the holdings overview on the landing ("portfolio") page. It plots the total value of every held creator key over time with 7d/30d/90d/all range selectors, an optional benchmark comparison line, a value+date tooltip, and the percentage return between the first and last visible data point. The series is fetched through a new portfolio-history service and React Query hook; the chart itself is presentational so it can be driven with fixtures in tests.

Related Issue

Implements the acceptance criteria for #1052.

Changes

  • [ADD] src/utils/portfolioHistory.utils.ts

    • PortfolioHistoryPoint type plus the four range options (7d/30d/90d/all) with their windows.
    • normalizeHistory (drops malformed samples, sorts ascending), filterHistoryByRange, computePortfolioReturnPercent and computeBenchmarkReturnPercent (first-to-last, null when undefined), and buildPortfolioChartSeries, which rebases the benchmark index onto the portfolio's opening value so both lines are comparable while the portfolio line keeps its real XLM values.
    • Tooltip/axis formatters (formatPortfolioValueXlm, formatPortfolioHistoryDate, formatPortfolioReturnPercent).
  • [ADD] src/services/portfolioHistory.service.ts

    • GET /users/:wallet/portfolio-history?range= via the shared BaseApiService, with the same short-lived cache pattern as the trade-history service. Responses are normalized before they reach the chart.
  • [ADD] src/hooks/usePortfolioHistory.ts

    • React Query wrapper keyed on queryKeys.wallet.portfolioHistory. Fetches the full series once so range switching is instant and offline.
  • [ADD] src/components/common/PortfolioPerformanceChart.tsx

    • Recharts line chart (the charting library already used by PriceHistoryChart — no new dependency).
    • Range selector group (aria-pressed buttons), benchmark toggle (disabled when the range carries no benchmark), value+date tooltip, and a signed percentage-return badge.
  • [MODIFY] src/lib/queryKeys.ts

    • Adds wallet.portfolioHistory(address, range).
  • [MODIFY] src/pages/LandingPage.tsx

    • Fetches the wallet's history with usePortfolioHistory(activeWalletAddress) and renders PortfolioPerformanceChart under the unrealised-P&L summary, wiring loading and error states.
  • [ADD] src/utils/__tests__/portfolioHistory.utils.test.ts

    • Range windows, boundary inclusion, malformed-sample handling, first-to-last return, benchmark return, benchmark rebasing, and formatters.
  • [ADD] src/components/common/__tests__/PortfolioPerformanceChart.test.tsx

    • Skeleton/empty states, first-to-last return for the default range, switching all four ranges, benchmark toggle on/off, disabled toggle without benchmark data, error + retry, and custom className.
  • [ADD] src/pages/__tests__/LandingPage.portfolioHistory.integration.test.tsx

    • Wires the chart into the page: asserts the history service is called, the return is rendered for the default range, the range selector recomputes it, and an empty series shows the empty state.

Verification Results

The repo could not be installed or its test suite executed in this environment (no clone / no pnpm install), so the new vitest files were not run. What was actually verified:

* TypeScript 5.9.3 (local toolchain) transpile/syntax check of every added and modified file: clean.
* Full tsc --strict --noUnusedLocals --noUnusedParameters typecheck of the standalone
  src/utils/portfolioHistory.utils.ts (no external imports): clean.
* Executed the compiled pure utils against 26 assertions covering range filtering,
  first-to-last return, benchmark return/rebasing, normalization and formatters: all pass.
* Traced the wiring: LandingPage -> usePortfolioHistory -> fetchPortfolioHistory ->
  GET /users/:wallet/portfolio-history?range=all, with the chart slicing client-side.
Acceptance Criteria Status
Chart renders correct portfolio value at each data point Chart plots each sample's XLM value; rebasing only affects the benchmark line
Range selector switches data correctly for all four periods 7d/30d/90d/all windows unit-tested, including the boundary sample
Benchmark comparison line toggled correctly Toggle + rebased benchmark series covered by unit and component tests
Tooltip shows correct value and date formatPortfolioValueXlm / formatPortfolioHistoryDate used by the tooltip and covered by tests
Percentage return computed correctly from first to last data point computePortfolioReturnPercent unit-tested for gains, ranges and degenerate series

Closes #1052

@drips-wave

drips-wave Bot commented Sep 28, 2026

Copy link
Copy Markdown

@sksneh Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Chucks1093
Chucks1093 merged commit 77e0530 into accesslayerorg:dev Sep 29, 2026
1 check passed
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.

Implement portfolio performance chart with total value over time

2 participants