Multi-strategy engine with per-strategy capital allocation
Adds MultiStrategyManager: it runs several strategies concurrently, each with its own TradingEngine seeded with starting_balance * allocation, so one strategy's losses cannot eat into another's capital. Weights are normalised across the run ([1,3] -> 25%/75%). Risk limits stay per strategy (each engine owns a RiskManager). New API: POST /api/bot/start-many and GET /api/bot/status. The existing single-strategy endpoints delegate to the manager, so their behaviour is unchanged. Adds manager tests (allocation, isolation, duplicate keys) and API tests.
This commit is contained in:
1 parent
e07536542f
commit
8b6cf8130c
6 files changed
+469
-28
No files matched your search
@@ -134,6 +134,18 @@ class StrategyConfigOut(BaseModel):
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class StrategySpec(BaseModel):
|
||||
"""One strategy in a multi-strategy run."""
|
||||
|
||||
strategy_key: str
|
||||
params: dict[str, Any] = Field(default_factory=dict)
|
||||
symbol: str | None = None
|
||||
timeframe: str | None = None
|
||||
# Fraction of the account allocated to this strategy (relative weights are
|
||||
# normalised across the run, so [1, 3] gives 25% / 75%).
|
||||
weight: float = Field(default=1.0, gt=0)
|
||||
|
||||
|
||||
class StartRequest(BaseModel):
|
||||
strategy_key: str
|
||||
symbol: str | None = None
|
||||
@@ -142,6 +154,44 @@ class StartRequest(BaseModel):
|
||||
starting_balance: float | None = None
|
||||
|
||||
|
||||
class StartManyRequest(BaseModel):
|
||||
strategies: list[StrategySpec] = Field(min_length=1)
|
||||
starting_balance: float | None = None
|
||||
|
||||
|
||||
class StrategyStatusOut(BaseModel):
|
||||
"""Per-strategy status inside a multi-strategy run."""
|
||||
|
||||
strategy_key: str
|
||||
strategy_name: str | None
|
||||
params: dict[str, Any] = Field(default_factory=dict)
|
||||
symbol: str
|
||||
timeframe: str
|
||||
allocation: float
|
||||
cash: float
|
||||
equity: float
|
||||
realized_pnl: float
|
||||
unrealized_pnl: float
|
||||
total_pnl: float
|
||||
open_positions: int
|
||||
halted: bool = False
|
||||
halt_reason: str | None = None
|
||||
trades_today: int = 0
|
||||
error: str | None = None
|
||||
|
||||
|
||||
class MultiStatusOut(BaseModel):
|
||||
running: bool
|
||||
mode: str
|
||||
exchange: str
|
||||
price: float | None = None
|
||||
starting_balance: float
|
||||
equity: float
|
||||
total_pnl: float
|
||||
strategies: list[StrategyStatusOut] = Field(default_factory=list)
|
||||
last_tick: datetime | None = None
|
||||
|
||||
|
||||
class ManualOrderRequest(BaseModel):
|
||||
side: Literal["buy", "sell"]
|
||||
qty: float | None = None
|
||||
|
||||
Reference in new issue
Block a user