Files
Cerbero-Bite/tests/unit/test_greeks_aggregator.py
T
Adriano fbb7753cc6 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>
2026-04-27 10:14:06 +02:00

101 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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")