main
py 49 lines 1.51 KB
Raw
1 """Future privacy boundary for short-lived customer portfolio context.
2
3 No implementation is selected in this iteration. Public company research uses
4 the canonical global instrument IDs projected from this context and remains
5 durable independently of customer quantities, costs, values, and P&L.
6 """
7 from __future__ import annotations
8
9 from dataclasses import dataclass
10 from datetime import datetime
11 from decimal import Decimal
12 from typing import Protocol
13 from uuid import UUID
14
15
16 @dataclass(frozen=True)
17 class PortfolioHoldingContext:
18 global_instrument_id: UUID
19 quantity: Decimal | None = None
20 average_cost: Decimal | None = None
21 market_value: Decimal | None = None
22 unrealized_pnl: Decimal | None = None
23
24
25 @dataclass(frozen=True)
26 class PortfolioContext:
27 context_id: str
28 owner_session_id: str
29 portfolio_id: UUID
30 holdings: tuple[PortfolioHoldingContext, ...]
31 expires_at: datetime
32
33 @property
34 def global_instrument_ids(self) -> tuple[UUID, ...]:
35 """Return a stable, deduplicated public-research projection."""
36 return tuple(dict.fromkeys(item.global_instrument_id for item in self.holdings))
37
38
39 class PortfolioContextStore(Protocol):
40 """Storage seam for Postgres DEV, memory tests, and Redis production later."""
41
42 async def load(self, owner_session_id: str, portfolio_id: UUID) -> PortfolioContext | None:
43 ...
44
45 async def save(self, context: PortfolioContext) -> None:
46 ...
47
48 async def delete(self, owner_session_id: str, portfolio_id: UUID) -> None:
49 ...