> ## Documentation Index
> Fetch the complete documentation index at: https://hedgeem-api.qeetoto.com/llms.txt
> Use this file to discover all available pages before exploring further.

# F015 — Payout Calculation

> How winnings are calculated at river — winning hand bets paid at locked-in odds

# F015 — Payout Calculation

| Field  | Value                     |
| ------ | ------------------------- |
| ID     | F015                      |
| Phase  | 2 — Betting               |
| Jira   | HEDGE-136                 |
| Status | TS ✅ fixed · JS ⬜ · UMA ⬜ |

## Business Rule

At river, the game resolves all bets:

```
payout = Σ(stakeAmount × oddsAtBet)   for all bets on WINNING hands, all stages
```

* **Winning hand only**: bets on losing hands and dead hands contribute £0
* **Locked-in odds**: `oddsAtBet` is the `oddsRounded` value at the moment the bet was placed, not the river odds
* **All stages included**: hole, flop, and turn bets on the winner all pay out
* **Credits**: `credits = (initialCredits − totalBet) + payout` at end of round

## Worked Example (from Game Concept Document)

> "If a hand has a 25% chance of winning this is first converted to European odds (4:1) then a house margin is subtracted of (say) 3% so odds become 3.88:1. If the player bets £1 on the hand and it wins they will receive £3.88 payout. If the player has four successive bets of same value statistically only one will win; thus in this case RTP = payout/bet = 3.88/4 = 0.97."

|                    | Value              |
| ------------------ | ------------------ |
| Win probability    | 25%                |
| Fair odds          | 4.00               |
| House margin       | 3%                 |
| `oddsAtBet`        | 3.88               |
| Stake              | £1.00 (100p)       |
| **Payout if wins** | **£3.88 (388p)**   |
| RTP (1 in 4 wins)  | 3.88 / 4 = **97%** |

The stake (£1.00) is deducted from credits at bet placement. On win, £3.88 is added — net gain £2.88.

## JavaScript Reference

**JS Source:** `hedgeem-v4/odobo/src/js/hands.js`

```javascript theme={null}
// hands.js:957 — ResolveHands()
var winAmount = 0;
for (var hand = 0; hand < NUMBER_OF_HANDS; hand++) {
    for (var bet = 0; bet < 3; bet++) {
        winAmount += GetHandOdds(hand, bet) * GetHandBet(hand, bet);
    }
}
SetWinAmount(winAmount);
```

`GetHandOdds(hand, stage)` returns the **locked-in** odds for that hand at that betting stage —
non-zero only for the winning hand; 0 for losing and dead hands. This is why the JS loop over all
hands naturally produces 0 for non-winners without an explicit winner check.

`GetHandBet(hand, stage)` returns the bet placed on that hand at that stage (0 if no bet).

**Credit deduction and award (control.js):**

```javascript theme={null}
// CC_DEDUCT_BETS — deducts total bets after final betting window
case CC_DEDUCT_BETS:
    SetCreditAmount(credits - GetBetAmount());

// CC_ADD_WIN — adds payout after resolution
case CC_ADD_WIN:
    SetCreditAmount(credits + GetWinAmount());
```

Note: JS deducts credits in a single batch at `CC_DEDUCT_BETS` (after all betting). TS deducts
per-bet immediately in `placeBet()`. Both produce the same net credit result.

## TypeScript Implementation

**File:** `standalone_reference_client/src/engine/GameEngine.ts`

```typescript theme={null}
resolveBets(): number {
    let totalPayout = 0;
    for (const bet of this.bets) {
        if (this.isHandWinner(bet.handIndex)) {
            totalPayout += Math.round(bet.stakeAmount * bet.oddsAtBet);
        }
    }
    this.credits += totalPayout;
    this.winAmount = totalPayout;
    return totalPayout;
}
```

`isHandWinner(hand)` reads `statusIsWinner` from the game record at `dealStatus=3` (river).

### Bug fixed (HEDGE-136)

The previous implementation paid ALL bets regardless of hand outcome. Winning-hand filter was missing.

## Acceptance Criteria

| #   | Criterion                                                     | JS | TS | UMA |
| --- | ------------------------------------------------------------- | -- | -- | --- |
| AC1 | Bet on winning hand → credits increase by `stake × oddsAtBet` | ⬜  | ✅  | ⬜   |
| AC2 | Bet on losing hand only → `winAmount = 0`                     | ⬜  | ✅  | ⬜   |
| AC3 | Bets on all hands (3-hand game) → `winAmount > 0`             | ⬜  | ✅  | ⬜   |
| AC4 | `credits = (startCredits − totalBet) + winAmount` at river    | ⬜  | ✅  | ⬜   |
| AC5 | Bets on dead hand → contribute £0 to payout                   | ⬜  | ✅  | ⬜   |

## Version Parity

| Version        | Status        | Notes                                              |
| -------------- | ------------- | -------------------------------------------------- |
| JS (reference) | ⬜ Not audited | `GetHandOdds` returns 0 for non-winners implicitly |
| TS             | ✅ Fixed       | HEDGE-136 — explicit `isHandWinner` guard added    |
| UMA            | ⬜ Not audited | Phase 5                                            |

## Test Coverage

| Test                       | Location                                                                      |
| -------------------------- | ----------------------------------------------------------------------------- |
| Full cycle — winner payout | `standalone_reference_client/tests/e2e/smoke.spec.ts` — `betting: full cycle` |
| Payout feature spec        | `tests/features/F015-payout-calculation.spec.ts` (to add)                     |
