---
name: composite-ev
description: Calculate expected value (EV) of multi-leg composite bets (LAY/BACK combinations) on greyhound races, with Betfair commission and per-outcome breakdown. Output is mathematics only — never a recommendation.
version: 0.1
category: quant
tags: [hermes, sig_dog_bot, opus, ev, betting-math, no-recommendation]
---

# Composite EV calculator

## Призначення

Чиста арифметика EV для composite-bet на одну race. Roman дає payload з ratings (p_win) + market (back/lay prices) + legs (LAY/BACK + stakes) + commission — отримує матрицю outcome→net→prob→contribution + total EV + Kelly fraction.

**Це не рекомендація.** Це числа. Decision робить Roman.

## Тригер

Slash-команда `/composite-ev` у Telegram DM з payload (формат нижче).

## Payload format

```
/composite-ev
race: <description, free text>
ratings:
  trap1: <p_win 0..1>
  trap2: <p_win>
  ...
market:
  trap1: {back: <price>, lay: <price>}
  trap2: {back: <price>, lay: <price>}
  ...
legs:
  LAY trap1 stake=<£>
  LAY trap5 stake=<£>
  BACK trap3 stake=<£>
commission: 0.025  (опціонально, default 0.025)
```

**Validation rules:**
- Σ ratings має наближатись до 1.0; якщо ні — нормалізуй і WARN
- Кожен runner у `legs` має існувати у `ratings` і `market`
- LAY price > BACK price (sanity); інакше WARN
- stake > 0, prices > 1.0
- Якщо `commission` відсутній → 0.025

## Workflow

1. Парсиш TG-message. Якщо payload неповний — попроси доповнити, **не додумуй**.
2. Перетвори у JSON-структуру (нижче — формат для Python helper).
3. Виклич Python helper:
   ```bash
   python3 ~/.hermes/skills/quant/composite-ev/scripts/composite_ev.py <(echo '<JSON>')
   ```
4. Output скрипта = JSON з полем `markdown_report` — поверни його у TG як є.

## JSON-формат для helper'а

```json
{
  "race": "Sheffield 19:32 6 May",
  "ratings": {"trap1": 0.18, "trap2": 0.22, "trap3": 0.27, "trap4": 0.12, "trap5": 0.13, "trap6": 0.08},
  "market": {
    "trap1": {"back": 5.4, "lay": 5.6},
    "trap2": {"back": 4.4, "lay": 4.6},
    "trap3": {"back": 3.7, "lay": 3.85},
    "trap4": {"back": 8.0, "lay": 8.6},
    "trap5": {"back": 7.4, "lay": 7.8},
    "trap6": {"back": 12.0, "lay": 13.0}
  },
  "legs": [
    {"side": "LAY",  "runner": "trap1", "stake": 10.0},
    {"side": "LAY",  "runner": "trap5", "stake": 10.0},
    {"side": "BACK", "runner": "trap3", "stake": 20.0}
  ],
  "commission": 0.025
}
```

## Output template (Markdown, фактичний output Python helper'а)

```
🧮 Composite EV — Sheffield 19:32 6 May

Legs:
  • LAY trap1 @ 5.60 stake £10.00  (liability £46.00)
  • LAY trap5 @ 7.80 stake £10.00  (liability £68.00)
  • BACK trap3 @ 3.70 stake £20.00

Outcome breakdown:
  trap1 (p=0.180) → net = -56.00 £   (×p = -10.08)
  trap2 (p=0.220) → net = +0.00 £    (×p = +0.00)
  trap3 (p=0.270) → net = +72.15 £   (×p = +19.48)
  trap4 (p=0.120) → net = +0.00 £    (×p = +0.00)
  trap5 (p=0.130) → net = -78.00 £   (×p = -10.14)
  trap6 (p=0.080) → net = +0.00 £    (×p = +0.00)

Σ EV (gross):                 £-0.24
Σ EV (after 2.5% commission): £-0.74

Total exposure: £134.00  (worst loss = £-78.00)
Kelly fraction: -0.55%

⚠️  Negative EV.

_Not a recommendation either way — your call._
```

## Edge cases

- **Σ ratings ≠ 1.0** → нормалізуй і WARN: *"ratings sum was X, normalized to 1.0"*
- **Жодний leg не покриває race** (e.g. only 1 LAY у 6-runner race) → видай результат + WARN: *"this is essentially a single-leg LAY — composite math degenerate"*
- **`commission` not set** → дефолт 0.025
- **Market inverted** (lay ≤ back) → WARN, продовжуй
- **Stake/price ≤ 0 або price ≤ 1.0** → reject з помилкою
- **Roman пише `/composite-ev` без payload** → відповідай з прикладом payload (повним) і список вимог

## Що НЕ робить skill

- ❌ Не оптимізує stakes ("яка пропорція дає max EV") — це окрема задача, потребує optimizer
- ❌ Не порівнює з конкуруючими стратегіями
- ❌ Не дає рекомендацій "ставити чи ні"
- ❌ Не fetchить live market дані (Roman дає snapshot вручну в payload)
