Skip to content

Add investment instrument discovery search API (#510, slice 1)#512

Merged
avresial merged 2 commits into
developfrom
claude/issue-510-changes-x5tmbc
Jun 27, 2026
Merged

Add investment instrument discovery search API (#510, slice 1)#512
avresial merged 2 commits into
developfrom
claude/issue-510-changes-x5tmbc

Conversation

@avresial

@avresial avresial commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

First, conservative increment of #510 (Add investment instrument discovery and import using OpenFIGI and Alpha Vantage). Issue #510 is size/XL, so per the repo's CLAUDE.md guidance ("if XL, split / implement incrementally") this PR delivers only the search foundation — no master-data is created and no UI is touched yet.

This is part of #510, not a full resolution, so there is intentionally no auto-close keyword.

What's included

  • InstrumentDiscoveryResultDto (Domain, new Assets/Discovery namespace): rich listing-level result — Source, DisplayName, Ticker, ExchangeMic/ExchangeCode/ExchangeName, TradingCurrency, ListingFigi/ShareClassFigi/CompositeFigi, Isin, SecurityType/MarketSector, ProviderSymbol, ConfidenceScore, Warnings. Deliberately distinct from the existing InstrumentSearchResultDto (which searches listings already in the DB) to avoid breaking the current transaction-form autocomplete.
  • IInvestmentInstrumentDiscoveryService + InvestmentInstrumentDiscoveryService (Application):
    • detects ISIN vs ticker/name queries;
    • ISIN → OpenFIGI MapByIsinAsync (listing-level results);
    • ticker/name → fans out to OpenFIGI ticker mapping and Alpha Vantage symbol search, then merges/de-duplicates;
    • prefers OpenFIGI for canonical identity, uses Alpha Vantage only for the price-symbol candidate;
    • assigns a confidence score (Combined 1.0, OpenFIGI-only 0.7, AV-only ≤0.6) and emits warnings for ambiguous/unknown exchange mappings, minor-unit (GBX) quoting, missing price symbols, and AV/listing currency mismatches;
    • reuses the existing BrokerSymbol venue mapping (no new EF table / migration in this slice);
    • caches results for 15 minutes; provider failures are swallowed (no raw provider exceptions reach the UI).
  • GET /api/investments/instruments/search?query= — new user-facing endpoint, separate from the admin asset CRUD API.
  • DI registration in AddFinancialAccountsApplication.
  • Unit tests (InvestmentInstrumentDiscoveryServiceTests): empty query, ISIN path, ticker/name merge → Combined, OpenFIGI-only normalization + missing-symbol warning, AV-only handling, GBX multiplier warning, ambiguous-exchange warning, and result caching.

Deferred to later slices

  • Import-preview / import endpoints, existing-record matching & de-duplication, and Alpha Vantage symbol price-validation (enable/disable on validation result).
  • The ExchangeMapping DB table/service/seed (this slice reuses the in-code BrokerSymbol mapping).
  • FIGI-query search path (requires extending IOpenFigiClient).
  • The Blazor search/import component and its integration into the investment transaction form and the admin asset page.

Validation

  • dotnet build — succeeds, 0 warnings (warnings-as-errors).
  • dotnet format — clean.
  • dotnet test (unit) — 493 passed, 0 failed.
  • No .razor/UI changes in this slice, so no screenshots.

https://claude.ai/code/session_01NiXYVP3Hth2rmGTPs7A6Xw


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added a new investment instrument search endpoint for authenticated users.
    • Search results now include richer details like exchange, identifier, confidence score, and warnings.
  • Bug Fixes

    • Empty searches now return no results instead of triggering lookups.
    • Results are better matched and de-duplicated across data sources, improving consistency.
    • Search responses now handle provider issues more gracefully and can fall back to an empty result set.
  • Tests

    • Added coverage for search behavior, result merging, caching, and exchange/currency warnings.

First increment of issue #510: a user-facing instrument discovery search
over OpenFIGI and Alpha Vantage, kept separate from the admin asset CRUD API.

- InstrumentDiscoveryResultDto (Domain): rich listing-level result with
  identity (FIGI/ISIN), exchange MIC/name, trading currency, provider symbol,
  source, confidence score and warnings. Distinct from the existing
  InstrumentSearchResultDto (which searches existing DB listings).
- IInvestmentInstrumentDiscoveryService + implementation (Application):
  detects ISIN vs ticker/name queries, fans out to OpenFIGI and Alpha Vantage,
  prefers OpenFIGI for identity, attaches the AV symbol as a price candidate,
  merges/de-duplicates, scores confidence, and emits warnings for ambiguous
  exchange mappings, GBX/minor-unit quoting, missing symbols and currency
  mismatches. Reuses the existing BrokerSymbol venue mapping; results cached
  15 minutes. Provider failures are swallowed (no raw exceptions to the UI).
- GET /api/investments/instruments/search endpoint.
- Unit tests for ISIN, ticker/name, OpenFIGI-only, AV-only, combined,
  GBX multiplier warning, ambiguous-exchange warning, and caching.

Import-preview/import (with symbol validation) and the Blazor search/import
UI are intentionally deferred to later slices.

Claude-Session: https://claude.ai/code/session_01NiXYVP3Hth2rmGTPs7A6Xw
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@avresial, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 16 minutes and 3 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a3f11c10-2915-4ef7-8c23-6d534bd8c19b

📥 Commits

Reviewing files that changed from the base of the PR and between 07ae619 and 2534a2d.

📒 Files selected for processing (4)
  • code/FinanceManager.Application/FinancialAccounts/Investments/Discovery/InvestmentInstrumentDiscoveryService.cs
  • code/FinanceManager.Domain/Assets/Discovery/InstrumentDiscoveryResultDto.cs
  • code/FinanceManager.Tests.Integration/Controllers/InvestmentInstrumentDiscoveryControllerTests.cs
  • code/FinanceManager.Tests.Unit/Application/Services/Discovery/InvestmentInstrumentDiscoveryServiceTests.cs

Walkthrough

Adds an authenticated investment instrument discovery endpoint, a discovery service that queries OpenFIGI and Alpha Vantage with caching and result normalization, a shared result DTO, DI registration, and unit tests covering search paths and warnings.

Changes

Investment instrument discovery

Layer / File(s) Summary
Public contract and result shape
code/FinanceManager.Application/FinancialAccounts/Investments/Discovery/IInvestmentInstrumentDiscoveryService.cs, code/FinanceManager.Domain/Assets/Discovery/InstrumentDiscoveryResultDto.cs
Defines the discovery search contract and the DTO used for normalized instrument results.
Search entry and wiring
code/FinanceManager.Application/FinancialAccounts/Investments/Discovery/InvestmentInstrumentDiscoveryService.cs, code/FinanceManager.Api/Controllers/Accounts/InvestmentInstrumentDiscoveryController.cs, code/FinanceManager.Application/FinancialAccounts/Registration.cs, code/FinanceManager.Tests.Unit/Application/Services/Discovery/InvestmentInstrumentDiscoveryServiceTests.cs
Implements cached query routing, the authenticated search endpoint, scoped DI registration, and tests for blank-query and ISIN behavior.
Result merging and validation
code/FinanceManager.Application/FinancialAccounts/Investments/Discovery/InvestmentInstrumentDiscoveryService.cs, code/FinanceManager.Tests.Unit/Application/Services/Discovery/InvestmentInstrumentDiscoveryServiceTests.cs
Builds merged OpenFIGI/Alpha Vantage results, applies venue and currency warnings, handles provider failures, and verifies combined, provider-only, exchange, and cache cases.

Sequence Diagram(s)

sequenceDiagram
  participant InvestmentInstrumentDiscoveryController
  participant InvestmentInstrumentDiscoveryService
  participant IMemoryCache
  participant IOpenFigiClient
  participant IAlphaVantageClient

  InvestmentInstrumentDiscoveryController->>InvestmentInstrumentDiscoveryService: SearchAsync(query, cancellationToken)
  InvestmentInstrumentDiscoveryService->>IMemoryCache: TryGetValue(cacheKey)

  alt ISIN query
    InvestmentInstrumentDiscoveryService->>IOpenFigiClient: MapByIsinAsync(query, ct)
  else ticker or name query
    InvestmentInstrumentDiscoveryService->>IOpenFigiClient: MapByTickerAsync(baseTicker, exchangeCode, ct)
    InvestmentInstrumentDiscoveryService->>IAlphaVantageClient: SearchSymbolsAsync(query, ct)
  end

  InvestmentInstrumentDiscoveryService-->>InvestmentInstrumentDiscoveryController: IReadOnlyList<InstrumentDiscoveryResultDto>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • Issue 510: This PR adds the discovery/search API and service described by the issue.

Suggested labels

size/L

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding the investment instrument discovery search API.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/issue-510-changes-x5tmbc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@code/FinanceManager.Application/FinancialAccounts/Investments/Discovery/InvestmentInstrumentDiscoveryService.cs`:
- Around line 111-117: The matching logic in
InvestmentInstrumentDiscoveryService is reusing the same Alpha Vantage symbol
across multiple OpenFIGI listings because FindMatchingAv still searches the full
avMatches set instead of excluding already-used entries. Update the matching
flow in the loop that builds each DTO, and in the other affected block, so it
filters out symbols already present in consumedAvSymbols before selecting a
match; this should ensure each AV symbol is attached to at most one listing and
prevent duplicate “Combined” confidence assignments.
- Around line 45-61: The outer catch in InvestmentInstrumentDiscoveryService’s
discovery flow is swallowing non-provider bugs and turning them into empty
results. Remove or narrow the general Exception handler around the
SearchByIsinAsync/SearchByTickerOrNameAsync path so only expected provider
failures are degraded, and let errors from Merge, ResolveVenue, DTO
construction, or caching propagate normally; keep the OperationCanceledException
handling intact and preserve the existing SafeOpenFigi/SafeAlphaVantage fallback
behavior.
- Around line 204-223: Normalize the Alpha Vantage fallback in
BuildFromAlphaVantage so AV-only matches return canonical instrument fields
instead of raw provider values. Use BrokerSymbol.TryLookupByVenueToken (via the
av.Region alias) to derive the venue metadata and populate ExchangeMic, and
separate the provider symbol from the display ticker so Ticker is normalized
while ProviderSymbol keeps av.Symbol. Keep the existing confidence/warnings
behavior, but ensure the returned InstrumentDiscoveryResultDto includes the
resolved exchange identity rather than leaving ExchangeMic unset.

In `@code/FinanceManager.Domain/Assets/Discovery/InstrumentDiscoveryResultDto.cs`:
- Around line 57-58: The InstrumentDiscoveryResultDto.Warnings property is
mutable, which allows callers to alter cached discovery results reused by
InvestmentInstrumentDiscoveryService.SearchAsync. Change Warnings to an
immutable/read-only type such as IReadOnlyList<string> or an immutable
collection, and update any construction or assignment code in
InstrumentDiscoveryResultDto and its consumers to produce the read-only value
without exposing a mutable List<string>.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0539c93d-389a-46ca-ac27-e43b5fbcc5c8

📥 Commits

Reviewing files that changed from the base of the PR and between f13f98d and 07ae619.

⛔ Files ignored due to path filters (1)
  • CHANGELOG.md is excluded by !**/*.md
📒 Files selected for processing (6)
  • code/FinanceManager.Api/Controllers/Accounts/InvestmentInstrumentDiscoveryController.cs
  • code/FinanceManager.Application/FinancialAccounts/Investments/Discovery/IInvestmentInstrumentDiscoveryService.cs
  • code/FinanceManager.Application/FinancialAccounts/Investments/Discovery/InvestmentInstrumentDiscoveryService.cs
  • code/FinanceManager.Application/FinancialAccounts/Registration.cs
  • code/FinanceManager.Domain/Assets/Discovery/InstrumentDiscoveryResultDto.cs
  • code/FinanceManager.Tests.Unit/Application/Services/Discovery/InvestmentInstrumentDiscoveryServiceTests.cs

Comment thread code/FinanceManager.Domain/Assets/Discovery/InstrumentDiscoveryResultDto.cs Outdated
…#510

Fixes the architecture test (EveryApiControllerHasIntegrationTests) that
required InvestmentInstrumentDiscoveryController to have integration coverage,
and applies CodeRabbit review feedback on the discovery service.

- Add InvestmentInstrumentDiscoveryControllerTests (integration): unauthorized,
  empty-query (no provider calls), and ticker search with mocked OpenFIGI/Alpha
  Vantage clients (no real provider calls).
- FindMatchingAv now skips Alpha Vantage symbols already consumed by an earlier
  listing, so one AV symbol is never attached to multiple listings (and no
  duplicate "Combined" results). Covered by a new unit test.
- Normalize AV-only results: resolve venue identity from the AV region, keep the
  base ticker as the display Ticker, and preserve the raw provider symbol on
  ProviderSymbol.
- Remove the broad outer catch in SearchAsync: provider failures are already
  degraded by SafeOpenFigi/SafeAlphaVantage, so only genuine bugs would have
  been swallowed into empty results. Cancellation still propagates.
- Make InstrumentDiscoveryResultDto.Warnings an IReadOnlyList<string> so cached
  results can't be mutated by callers.

Claude-Session: https://claude.ai/code/session_01NiXYVP3Hth2rmGTPs7A6Xw
@avresial avresial marked this pull request as ready for review June 27, 2026 10:25
@avresial avresial merged commit d6d7648 into develop Jun 27, 2026
11 checks passed
@avresial avresial deleted the claude/issue-510-changes-x5tmbc branch June 27, 2026 11:22
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.

2 participants