feat: Consolidate portfolio and position ops into portfolio block#644
feat: Consolidate portfolio and position ops into portfolio block#644
Conversation
|
@claude please review this PR |
|
Claude finished @jfrench9's task in 3m 22s —— View job PR Review: feat: Consolidate portfolio and position ops into portfolio blockTasks:
Overall this is a clean, well-structured refactor. The "molecule envelope" abstraction is the right call — treating portfolio + positions as an atomic unit removes a whole class of partial-write bugs that atom-level CRUD invites. The command layer, model definitions, and router are all well organized. A few issues to address before merge: Bugs1. Cost accumulation before security guard in for p in positions:
sec = sec_map.get(p.security_id)
...
total_cost += cost_dollars # ← always accumulated
if value_dollars is not None:
total_value += value_dollars
has_value = True
if sec is None: # ← skip position_blocks.append
continue
position_blocks.append(...)If a position's security is absent from 2. Timezone-naive row.disposition_date = date.today() # ← local system timeThe line just below uses row.disposition_date = datetime.now(UTC).date()Design observations3. Three separate queries in The function makes three queries: positions_to_delete = (
session.execute(select(Position).where(...)).scalars().all()
)
for row in positions_to_delete:
session.delete(row)
session.delete(portfolio)
session.flush()
return DeletePortfolioBlockResponse(
deleted=True,
portfolio_id=body.portfolio_id,
positions_deleted=len(positions_to_delete), # no separate count query needed
)4. raise SecurityNotFoundError(sorted(missing)[0])When a caller provides 5. lambda e: ( # type: ignore[attr-defined]
f"Portfolio has {e.active_count} active position(s); ..."
),The Minor6. Bare sec_map: dict = {}
entity_map: dict = {}These should carry specific type arguments consistent with the rest of the codebase: 7. New portfolio block types missing from The 8. The pydantic-derived What's well done
|
… types, improve error handling, and update position deletion logic
Summary
Introduces the Portfolio Block abstraction, consolidating the previously separate
portfoliosandpositionscommand/read modules into a unifiedportfolio_blockmodule. This refactor simplifies the roboinvestor domain by treating portfolio and position operations as a single cohesive unit, alongside updates to the investor API model, GraphQL layer, and router operations.Key Accomplishments
New Portfolio Block module: Added
portfolio_block.pyfor both commands and reads, replacing the separateportfolios.pyandpositions.pymodules. The new command module (279 lines) and read module (135 lines) provide a unified interface for managing portfolios and their associated positions as a block.Investor model refactoring: Significantly reworked
robosystems/models/api/extensions/investor.py(~256 lines changed) to align with the portfolio block concept, updating data structures and validation logic.GraphQL layer updates: Extended
investor.pytypes with new GraphQL type definitions (37 new lines) and updated resolvers to support the portfolio block pattern.Router simplification: Refactored
roboinvestor/operations.pyrouter to use the new portfolio block abstraction, resulting in a cleaner and more maintainable API surface.Dependency update: Bumped
robosystems-clientto version 0.3.17 in optional dependencies.Breaking Changes
commands/portfolios.pyandcommands/positions.pyhave been deleted and replaced bycommands/portfolio_block.py. Any code importing from these modules will break.Testing
test_portfolio_block.py(242 lines) andtest_portfolio_block_commands.py(438 lines) provide comprehensive coverage for the new portfolio block reads and commands.test_portfolios.py(215 lines) andtest_positions.py(337 lines) were deleted along with their corresponding modules.Infrastructure Considerations
robosystems-clientdependency version has been updated, requiring a lock file refresh. Environments using the optional dependency group should be rebuilt to pick up the new client version.🤖 Generated with Claude Code
Branch Info:
feature/portfolio-blockmainCo-Authored-By: Claude noreply@anthropic.com