Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ dependencies = [
[project.optional-dependencies]
dev = [
# RoboSystems client for demo and testing
"robosystems-client==0.3.16",
"robosystems-client==0.3.17",

# Testing framework
"pytest>=8.4.0,<9.0",
Expand Down
39 changes: 22 additions & 17 deletions robosystems/graphql/resolvers/investor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
)
from robosystems.graphql.types.investor import (
HoldingsList,
Portfolio,
PortfolioBlock,
PortfolioList,
Position,
PositionList,
Expand All @@ -32,6 +32,9 @@
from robosystems.operations.roboinvestor.reads import (
holdings as reads_holdings,
)
from robosystems.operations.roboinvestor.reads import (
portfolio_block as reads_portfolio_block,
)
from robosystems.operations.roboinvestor.reads import (
portfolios as reads_portfolios,
)
Expand Down Expand Up @@ -88,22 +91,6 @@ def portfolios(
_raise_investor_not_initialized()
return PortfolioList.from_pydantic(response)

@strawberry.field
def portfolio(
self,
info: Info[GraphQLContext, None],
portfolio_id: str,
) -> Portfolio | None:
"""Single portfolio by id."""
try:
with _open_session(info, "roboinvestor") as session:
response = reads_portfolios.get_portfolio(session, portfolio_id)
except (ValueError, ProgrammingError):
_raise_investor_not_initialized()
if response is None:
return None
return Portfolio.from_pydantic(response)

# ── Securities ──────────────────────────────────────────────────────────

@strawberry.field
Expand Down Expand Up @@ -209,3 +196,21 @@ def holdings(
except (ValueError, ProgrammingError):
_raise_investor_not_initialized()
return HoldingsList.from_pydantic(response)

# ── Portfolio Block (molecule envelope) ─────────────────────────────────

@strawberry.field
def portfolio_block(
self,
info: Info[GraphQLContext, None],
portfolio_id: str,
) -> PortfolioBlock | None:
"""Portfolio-centric molecule: portfolio + active positions + securities + entities."""
try:
with _open_session(info, "roboinvestor") as session:
response = reads_portfolio_block.get_portfolio_block(session, portfolio_id)
except reads_holdings.PortfolioNotFoundError:
return None
except (ValueError, ProgrammingError):
_raise_investor_not_initialized()
return PortfolioBlock.from_pydantic(response)
45 changes: 45 additions & 0 deletions robosystems/graphql/types/investor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
from strawberry.scalars import JSON

from robosystems.graphql.types.common import PaginationInfo
from robosystems.models.api.extensions.investor import (
EntityLite as PydanticEntityLite,
)
from robosystems.models.api.extensions.investor import (
HoldingResponse as PydanticHoldingResponse,
)
Expand All @@ -24,12 +27,18 @@
from robosystems.models.api.extensions.investor import (
HoldingsListResponse as PydanticHoldingsListResponse,
)
from robosystems.models.api.extensions.investor import (
PortfolioBlockEnvelope as PydanticPortfolioBlockEnvelope,
)
from robosystems.models.api.extensions.investor import (
PortfolioListResponse as PydanticPortfolioListResponse,
)
from robosystems.models.api.extensions.investor import (
PortfolioResponse as PydanticPortfolioResponse,
)
from robosystems.models.api.extensions.investor import (
PositionBlock as PydanticPositionBlock,
)
from robosystems.models.api.extensions.investor import (
PositionListResponse as PydanticPositionListResponse,
)
Expand All @@ -39,6 +48,9 @@
from robosystems.models.api.extensions.investor import (
SecurityListResponse as PydanticSecurityListResponse,
)
from robosystems.models.api.extensions.investor import (
SecurityLite as PydanticSecurityLite,
)
from robosystems.models.api.extensions.investor import (
SecurityResponse as PydanticSecurityResponse,
)
Expand Down Expand Up @@ -152,3 +164,36 @@ class Holding:
)
class HoldingsList:
"""Full holdings view for a portfolio."""


# ── Portfolio Block (molecule envelope) ───────────────────────────────────


@strawberry.experimental.pydantic.type(model=PydanticEntityLite, all_fields=True)
class EntityLite:
"""Lightweight entity reference."""

id: strawberry.ID


@strawberry.experimental.pydantic.type(model=PydanticSecurityLite, all_fields=True)
class SecurityLite:
"""Lightweight security with issuer and cross-graph reference."""

id: strawberry.ID


@strawberry.experimental.pydantic.type(model=PydanticPositionBlock, all_fields=True)
class PositionBlock:
"""A position with its embedded security."""

id: strawberry.ID


@strawberry.experimental.pydantic.type(
model=PydanticPortfolioBlockEnvelope, all_fields=True
)
class PortfolioBlock:
"""Portfolio-centric molecule envelope — portfolio + positions + securities + entities."""

id: strawberry.ID
36 changes: 28 additions & 8 deletions robosystems/models/api/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,30 @@
ClosingBookStructuresResponse,
)
from .investor import (
CreatePortfolioRequest,
CreatePositionRequest,
CreatePortfolioBlockRequest,
CreateSecurityRequest,
DeletePortfolioBlockOperation,
DeletePortfolioBlockResponse,
EntityLite,
HoldingResponse,
HoldingSecuritySummary,
HoldingsListResponse,
PortfolioBlockEnvelope,
PortfolioBlockPortfolioFields,
PortfolioBlockPortfolioPatch,
PortfolioBlockPositionAdd,
PortfolioBlockPositionDispose,
PortfolioBlockPositions,
PortfolioBlockPositionUpdate,
PortfolioListResponse,
PortfolioResponse,
PositionBlock,
PositionListResponse,
PositionResponse,
SecurityListResponse,
SecurityLite,
SecurityResponse,
UpdatePortfolioRequest,
UpdatePositionRequest,
UpdatePortfolioBlockOperation,
UpdateSecurityRequest,
)
from .reports import (
Expand Down Expand Up @@ -75,10 +85,12 @@
"ClosingBookCategory",
"ClosingBookItem",
"ClosingBookStructuresResponse",
"CreatePortfolioRequest",
"CreatePositionRequest",
"CreatePortfolioBlockRequest",
"CreateReportRequest",
"CreateSecurityRequest",
"DeletePortfolioBlockOperation",
"DeletePortfolioBlockResponse",
"EntityLite",
"FactRowResponse",
"FinancialStatementAnalysisRequest",
"FinancialStatementAnalysisResponse",
Expand All @@ -94,15 +106,24 @@
"LiveFinancialStatementRequest",
"LiveFinancialStatementResponse",
"LiveStatementFactRow",
"PortfolioBlockEnvelope",
"PortfolioBlockPortfolioFields",
"PortfolioBlockPortfolioPatch",
"PortfolioBlockPositionAdd",
"PortfolioBlockPositionDispose",
"PortfolioBlockPositionUpdate",
"PortfolioBlockPositions",
"PortfolioListResponse",
"PortfolioResponse",
"PositionBlock",
"PositionListResponse",
"PositionResponse",
"RegenerateReportRequest",
"ReportListResponse",
"ReportResponse",
"ResolvedReportInfo",
"SecurityListResponse",
"SecurityLite",
"SecurityResponse",
"ShareReportRequest",
"ShareReportResponse",
Expand All @@ -111,8 +132,7 @@
"StructureSummary",
"TrialBalanceResponse",
"TrialBalanceRow",
"UpdatePortfolioRequest",
"UpdatePositionRequest",
"UpdatePortfolioBlockOperation",
"UpdateSecurityRequest",
"ValidationCheckResponse",
"cents_to_dollars",
Expand Down
Loading
Loading