feat: add strategy set CRUD operations (API + CLI)
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
"""Strategy set service orchestration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import Depends
|
||||
|
||||
from app.core.config import Settings, get_settings
|
||||
from app.domain.models import StrategySet
|
||||
from app.repositories.factory import get_portfolio_repository_from_settings
|
||||
from app.repositories.portfolio_repository import PortfolioRepository
|
||||
from app.schemas.portfolio import CreateStrategySetRequest, UpdateStrategySetRequest
|
||||
|
||||
|
||||
def get_strategy_set_repository(settings: Settings = Depends(get_settings)) -> PortfolioRepository:
|
||||
return get_portfolio_repository_from_settings(settings)
|
||||
|
||||
|
||||
class StrategySetService:
|
||||
def __init__(self, repository: PortfolioRepository) -> None:
|
||||
self._repository = repository
|
||||
|
||||
def list(self) -> list[StrategySet]:
|
||||
"""List all strategy sets."""
|
||||
return self._repository.list_strategy_sets()
|
||||
|
||||
def get(self, strategy_set_id: str) -> StrategySet | None:
|
||||
"""Get a single strategy set by ID."""
|
||||
return self._repository.get_strategy_set(strategy_set_id)
|
||||
|
||||
def create(self, payload: CreateStrategySetRequest) -> StrategySet:
|
||||
"""Create or get existing strategy set.
|
||||
|
||||
Uses get_or_create_strategy_set for idempotence.
|
||||
"""
|
||||
return self._repository.get_or_create_strategy_set(payload.name, payload.config)
|
||||
|
||||
def update(self, strategy_set_id: str, payload: UpdateStrategySetRequest) -> StrategySet:
|
||||
"""Update a strategy set.
|
||||
|
||||
Since strategy sets are identified by (name, config) uniqueness,
|
||||
updating requires creating a new one if name/config changes.
|
||||
"""
|
||||
current = self.get(strategy_set_id)
|
||||
if current is None:
|
||||
raise ValueError(f"Strategy set {strategy_set_id} not found")
|
||||
|
||||
# Use provided values or fall back to current ones
|
||||
new_name = payload.name if payload.name is not None else current.name
|
||||
new_config = payload.config if payload.config is not None else current.config
|
||||
|
||||
# If nothing changed, return current
|
||||
if new_name == current.name and new_config == current.config:
|
||||
return current
|
||||
|
||||
# Create or get the new strategy set
|
||||
new_strategy_set = self._repository.get_or_create_strategy_set(new_name, new_config)
|
||||
|
||||
# If the new strategy set is different from current, delete the old one
|
||||
# (only if it's not in use - delete_strategy_set will raise if in use)
|
||||
if new_strategy_set.id != strategy_set_id:
|
||||
try:
|
||||
self._repository.delete_strategy_set(strategy_set_id)
|
||||
except ValueError:
|
||||
# If old strategy set is in use, keep it and return new one
|
||||
pass
|
||||
|
||||
return new_strategy_set
|
||||
|
||||
def delete(self, strategy_set_id: str) -> None:
|
||||
"""Delete a strategy set.
|
||||
|
||||
Raises ValueError if the strategy set is in use by any portfolio.
|
||||
"""
|
||||
self._repository.delete_strategy_set(strategy_set_id)
|
||||
Reference in New Issue
Block a user