feat: math core v3 engine upgrade

This commit is contained in:
Celes Renata
2026-06-27 12:21:41 +00:00
parent 365bc5d4b7
commit b4bf0f2361
34 changed files with 11693 additions and 3 deletions
+111
View File
@@ -4,11 +4,14 @@ Computes dollar allocation and share quantity for a trade by applying
a sequential adjustment pipeline: confidence gate, correlation reduction,
sector exposure, diversification bonus, earnings proximity, portfolio
heat check, active-pool minimum, absolute cap, and share rounding.
Also provides v3 fractional Kelly position sizing (Requirements 14.114.7).
"""
from __future__ import annotations
import math
from dataclasses import dataclass
from datetime import datetime, timezone
from services.trading.models import (
@@ -345,3 +348,111 @@ class PositionSizer:
return new_dollar, new_pct
return dollar_amount, allocation_pct
# ===========================================================================
# v3 Fractional Kelly Position Sizing (Requirements 14.114.7)
# ===========================================================================
@dataclass(frozen=True)
class KellySizingResult:
"""Result of v3 fractional Kelly position sizing computation."""
portfolio_pct: float
f_kelly: float
reward_ratio: float
downgrade: bool
downgrade_reason: str
def _clamp(value: float, lo: float, hi: float) -> float:
"""Clamp value to [lo, hi]."""
return max(lo, min(hi, value))
def compute_reward_ratio(
confidence: float, strength: float, contradiction: float
) -> float:
"""Compute reward ratio b = clamp(1.2 + 2.0*confidence + 1.0*strength - contradiction, 1.2, 3.0).
Requirements: 14.2
"""
raw = 1.2 + 2.0 * confidence + 1.0 * strength - contradiction
return _clamp(raw, 1.2, 3.0)
def compute_kelly_sizing(
p_win: float,
b: float,
confidence: float,
data_quality: float,
contradiction: float,
max_position_pct: float,
available_caps: dict[str, float],
) -> KellySizingResult:
"""Compute fractional Kelly position sizing.
f_kelly = (p_win * b - (1 - p_win)) / b
portfolio_pct = clamp(max(0, f_kelly) * 0.25 * confidence * data_quality * (1 - contradiction), 0, max_position_pct)
Apply min of all capacity constraints from available_caps:
- sector_capacity
- correlation_capacity (0 if avg corr > 0.80)
- heat_capacity
Downgrade rules:
- If f_kelly <= 0 → portfolio_pct = 0, downgrade with reason "negative_edge"
- If portfolio_pct < 0.005 → downgrade with reason "position_below_minimum"
Requirements: 14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7
"""
# Compute Kelly fraction (Req 14.3)
f_kelly = (p_win * b - (1.0 - p_win)) / b
# Negative edge → immediate downgrade (Req 14.7)
if f_kelly <= 0.0:
return KellySizingResult(
portfolio_pct=0.0,
f_kelly=f_kelly,
reward_ratio=b,
downgrade=True,
downgrade_reason="negative_edge",
)
# Apply fractional Kelly with dampening factors (Req 14.4)
raw_pct = f_kelly * 0.25 * confidence * data_quality * (1.0 - contradiction)
portfolio_pct = _clamp(max(0.0, raw_pct), 0.0, max_position_pct)
# Apply capacity constraints (Req 14.5)
sector_capacity = available_caps.get("sector_capacity", max_position_pct)
correlation_capacity = available_caps.get("correlation_capacity", max_position_pct)
heat_capacity = available_caps.get("heat_capacity", max_position_pct)
# Correlation capacity of 0 means avg corr > 0.80 → force zero
portfolio_pct = min(
portfolio_pct,
max_position_pct,
sector_capacity,
correlation_capacity,
heat_capacity,
)
# Position below minimum threshold → downgrade (Req 14.6)
if portfolio_pct < 0.005:
return KellySizingResult(
portfolio_pct=0.0,
f_kelly=f_kelly,
reward_ratio=b,
downgrade=True,
downgrade_reason="position_below_minimum",
)
return KellySizingResult(
portfolio_pct=portfolio_pct,
f_kelly=f_kelly,
reward_ratio=b,
downgrade=False,
downgrade_reason="",
)