Phase 1: core algorithms
Implementa i sette algoritmi puri di docs/03-algorithms.md con disciplina TDD: 112 test, copertura statement+branch al 100% su core/ e config/, mypy --strict pulito, ruff pulito. Moduli: - config/schema.py: StrategyConfig Pydantic v2 con validatori di consistenza (kelly, delta, OTM, spread width, profit/stop). - core/types.py: OptionQuote e OptionLeg condivisi. - core/entry_validator.py: validate_entry (accumula motivi) e compute_bias (bull_put/bear_call/iron_condor/None). - core/liquidity_gate.py: check OI/volume/spread/depth + slippage stimato in % del credito. - core/sizing_engine.py: Quarter Kelly con cap 200/1000 EUR e bande DVOL. - core/combo_builder.py: select_strikes (DTE/OTM/delta/width/credit) e build (ComboProposal con credit/max_loss/breakeven). - core/greeks_aggregator.py: somma firmata BUY/SELL, theta in USD. - core/exit_decision.py: 6 trigger ordinati con eccezione skip-time vicino a profit (mark in (50%,70%] credito). - core/kelly_recalibration.py: full/quarter Kelly, confidence per sample size, blend medio in fascia 30-99 trade. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
"""TDD for :mod:`cerbero_bite.core.greeks_aggregator`.
|
||||
|
||||
Spec: ``docs/03-algorithms.md §5``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from cerbero_bite.core.greeks_aggregator import aggregate
|
||||
from cerbero_bite.core.types import OptionLeg
|
||||
|
||||
|
||||
def _leg(
|
||||
*,
|
||||
side: str,
|
||||
size: int = 1,
|
||||
delta: str,
|
||||
gamma: str = "0.001",
|
||||
theta: str = "-0.0005",
|
||||
vega: str = "0.10",
|
||||
strike: str = "2400",
|
||||
option_type: str = "P",
|
||||
instrument: str = "ETH-X-2400-P",
|
||||
) -> OptionLeg:
|
||||
return OptionLeg(
|
||||
instrument=instrument,
|
||||
side=side, # type: ignore[arg-type]
|
||||
strike=Decimal(strike),
|
||||
expiry=datetime(2026, 5, 15, 8, 0, tzinfo=UTC),
|
||||
type=option_type, # type: ignore[arg-type]
|
||||
size=size,
|
||||
mid_price_eth=Decimal("0.012"),
|
||||
delta=Decimal(delta),
|
||||
gamma=Decimal(gamma),
|
||||
theta=Decimal(theta),
|
||||
vega=Decimal(vega),
|
||||
)
|
||||
|
||||
|
||||
def test_bull_put_spread_net_delta_long_positive() -> None:
|
||||
"""SELL put (negative delta) + BUY further-OTM put → net delta positive."""
|
||||
legs = [
|
||||
_leg(side="SELL", delta="-0.12"),
|
||||
_leg(side="BUY", delta="-0.08", strike="2300"),
|
||||
]
|
||||
res = aggregate(legs=legs, eth_price_usd=Decimal("3000"))
|
||||
# delta_net = -1*-0.12 + +1*-0.08 = 0.12 - 0.08 = +0.04
|
||||
assert res.delta_net == Decimal("0.04")
|
||||
|
||||
|
||||
def test_bear_call_spread_net_delta_short_negative() -> None:
|
||||
legs = [
|
||||
_leg(side="SELL", delta="0.12", option_type="C"),
|
||||
_leg(side="BUY", delta="0.08", option_type="C", strike="3650"),
|
||||
]
|
||||
res = aggregate(legs=legs, eth_price_usd=Decimal("3000"))
|
||||
# delta_net = -1*0.12 + +1*0.08 = -0.04
|
||||
assert res.delta_net == Decimal("-0.04")
|
||||
|
||||
|
||||
def test_size_scales_each_leg() -> None:
|
||||
legs = [
|
||||
_leg(side="SELL", size=3, delta="-0.12"),
|
||||
_leg(side="BUY", size=3, delta="-0.08", strike="2300"),
|
||||
]
|
||||
res = aggregate(legs=legs, eth_price_usd=Decimal("3000"))
|
||||
assert res.delta_net == Decimal("0.12") # 3 × 0.04
|
||||
|
||||
|
||||
def test_theta_converted_to_usd_per_day() -> None:
|
||||
legs = [
|
||||
_leg(side="SELL", delta="-0.12", theta="-0.0005"), # selling → +0.0005 ETH theta
|
||||
_leg(side="BUY", delta="-0.08", theta="-0.0003", strike="2300"), # +0.0003 cost
|
||||
]
|
||||
res = aggregate(legs=legs, eth_price_usd=Decimal("3000"))
|
||||
# theta_eth = -1*(-0.0005) + +1*(-0.0003) = 0.0005 - 0.0003 = 0.0002
|
||||
# theta_usd = 0.0002 × 3000 = 0.60
|
||||
assert res.theta_net == Decimal("0.60")
|
||||
|
||||
|
||||
def test_gamma_and_vega_summed_with_sign() -> None:
|
||||
legs = [
|
||||
_leg(side="SELL", delta="-0.12", gamma="0.0020", vega="0.30"),
|
||||
_leg(side="BUY", delta="-0.08", gamma="0.0015", vega="0.20", strike="2300"),
|
||||
]
|
||||
res = aggregate(legs=legs, eth_price_usd=Decimal("3000"))
|
||||
# gamma: -1*0.0020 + 1*0.0015 = -0.0005
|
||||
# vega: -1*0.30 + 1*0.20 = -0.10
|
||||
assert res.gamma_net == Decimal("-0.0005")
|
||||
assert res.vega_net == Decimal("-0.10")
|
||||
|
||||
|
||||
def test_empty_legs_returns_zero_greeks() -> None:
|
||||
res = aggregate(legs=[], eth_price_usd=Decimal("3000"))
|
||||
assert res.delta_net == Decimal("0")
|
||||
assert res.gamma_net == Decimal("0")
|
||||
assert res.theta_net == Decimal("0")
|
||||
assert res.vega_net == Decimal("0")
|
||||
Reference in New Issue
Block a user