Skip to content

Commit 1b73fba

Browse files
Mlaz-codeclaude
andauthored
docs(ev): correct kelly_percent units (0-100, not 0.0-1.0) + full-Kelly safety note (#207)
Issue 1 from #237: docs described kelly_percent as a fraction (0.0-1.0) but the API actually emits a percentage (0-100). A consumer reading the docs literally would interpret 34.43 as "3,443% of bankroll" or crash on values >1.0. Fixes the schema in 6 doc pages: - api-reference/opportunities-ev.mdx — schema row, 2 example values, Kelly Criterion section (formula + sizing guide rewritten in 0-100 units) - quickstart.mdx — example value 0.0218 -> 2.18 - api-reference/websocket.mdx — 2 example values 0.038 -> 3.8 - api-reference/stream.mdx — example value 0.038 -> 3.8 - examples/value-betting.mdx — Discord embed + Telegram template now format as percentage with .toFixed(2) - sdks/python.mdx — print formatters changed from {:.1%} (which multiplies by 100, wrong for already-percentage values) to {:.2f}% Adds an explicit safety callout in the Kelly Criterion section explaining that the field is full Kelly with no quality-of-signal adjustment, and recommending fractional Kelly (1/4 or 1/2) plus 1-2% bankroll cap when warnings is non-empty or confidence_score < 80. This addresses the secondary concern in #237 with documentation rather than a wire change. Issue 2 (publishing full Kelly without quality adjustment) requires a product call on whether to: a) keep emitting full Kelly + add a kelly_percent_adjusted field, or b) change kelly_percent in place + add kelly_percent_raw Both options affect the wire and need migration discussion. Documenting the safety note now is the minimum correct action. Closes #237 (Issue 1) Refs #237 (Issue 2 — needs product decision) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3388aff commit 1b73fba

6 files changed

Lines changed: 25 additions & 19 deletions

File tree

content/en/api-reference/opportunities-ev.mdx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ for opp in data['data']:
129129
"start_time": "2026-02-08T19:00:00Z",
130130
"is_live": false,
131131
"confidence_score": 87,
132-
"kelly_percent": 0.021,
132+
"kelly_percent": 2.1,
133133
"book_count": 5,
134134
"arb_available": false,
135135
"arb_profit": null,
@@ -164,7 +164,7 @@ for opp in data['data']:
164164
"start_time": "2026-02-08T19:00:00Z",
165165
"is_live": false,
166166
"confidence_score": 82,
167-
"kelly_percent": 0.015,
167+
"kelly_percent": 1.5,
168168
"book_count": 5,
169169
"arb_available": false,
170170
"arb_profit": null,
@@ -294,7 +294,7 @@ X-Request-Id: req_abc123def456
294294
| `start_time` | string\|null | ISO 8601 event start time |
295295
| `is_live` | boolean | Whether the event is currently live |
296296
| `confidence_score` | number | Multi-factor confidence score (0-100) |
297-
| `kelly_percent` | number\|null | Kelly criterion optimal bet fraction (0.0 to 1.0) |
297+
| `kelly_percent` | number\|null | Full-Kelly optimal bet **percentage** of bankroll (0–100, e.g. `2.1` = 2.1% of bankroll). Most practitioners apply a fractional Kelly multiplier (¼ or ½) before sizing — see [Kelly Criterion](#kelly-criterion) below. |
298298
| `book_count` | number | Number of sportsbooks offering this market |
299299
| `arb_available` | boolean | Whether an arbitrage exists on this market |
300300
| `arb_profit` | number\|null | Arbitrage profit percentage if available |
@@ -391,20 +391,26 @@ We recommend setting `min_ev=2` for most use cases. Marginal EV (below 2%) can b
391391

392392
## Kelly Criterion
393393

394-
The `kelly_percent` field tells you the optimal fraction of your bankroll to wager according to the Kelly criterion (0.0 to 1.0, e.g. `0.021` = 2.1%):
394+
The `kelly_percent` field is the optimal **percentage** of your bankroll (0–100) to wager according to the full Kelly criterion. A value of `2.1` means full Kelly recommends 2.1% of bankroll; a value of `34.4` means 34.4%.
395395

396396
```
397-
Kelly% = (edge / odds_to_1) = (fair_prob x decimal_odds - 1) / (decimal_odds - 1)
397+
Kelly% = (fair_prob × decimal_odds - 1) / (decimal_odds - 1) × 100
398398
```
399399

400+
<Callout type="warning">
401+
**This is full Kelly, computed from the model's fair probability with no quality-of-signal adjustment.** Full Kelly is mathematically optimal only when the true probability is known exactly. In practice, sharp anchors carry uncertainty (single sharp reference, late-arriving live odds, low cross-validation), and full Kelly can produce dangerously large stake suggestions. See `confidence_score`, `cross_ref_count`, and `warnings` (e.g. `SINGLE_SHARP_REF`, `LIVE_STALE_ODDS`) before sizing.
402+
403+
**Use a fractional Kelly multiplier**: most practitioners apply ¼ or ½ Kelly (`kelly_percent × 0.25` or `× 0.5`). Cap any single bet at 1–2% of bankroll regardless of what `kelly_percent` reports, especially when `warnings` is non-empty or `confidence_score < 80`.
404+
</Callout>
405+
400406
### Kelly Sizing Guide
401407

402-
| Kelly % | Risk Level | Recommendation |
408+
| `kelly_percent` | Risk Level | Recommendation |
403409
|---------|------------|----------------|
404-
| < 1% | Low | Small edge, consider skipping |
405-
| 1 - 3% | Moderate | Standard bet size |
406-
| 3 - 5% | Aggressive | Strong edge, but size carefully |
407-
| 5%+ | Very aggressive | Excellent edge; consider fractional Kelly (half or quarter) |
410+
| < 1 | Low | Small edge, consider skipping |
411+
| 1 – 3 | Moderate | Standard bet size — quarter Kelly = 0.25–0.75% of bankroll |
412+
| 3 – 5 | Aggressive | Strong edge — quarter Kelly = 0.75–1.25% of bankroll |
413+
| 5+ | Very aggressive | Excellent edge or sharp-signal artifact; cap at 1–2% of bankroll regardless |
408414

409415
<Callout type="warning">
410416
**Variance Warning:** +EV does not guarantee profit on every bet. Over 100 bets at 5% EV, actual results can range widely. The edge only materializes over hundreds or thousands of bets. Never bet more than you can afford to lose.
@@ -413,7 +419,7 @@ Kelly% = (edge / odds_to_1) = (fair_prob x decimal_odds - 1) / (decimal_odds - 1
413419
## Best Practices
414420

415421
1. **Set a minimum EV threshold** - Use `min_ev=2` or higher to focus on meaningful edges
416-
2. **Use Kelly sizing** - Use `kelly_percent` to determine bet size (multiply by bankroll)
422+
2. **Use Kelly sizing** - `kelly_percent` is a percentage (e.g. `2.1` = 2.1% of bankroll). Apply a fractional Kelly multiplier (¼ or ½) before sizing
417423
3. **Filter by confidence** - Use `confidence_score` to prioritize high-confidence opportunities
418424
4. **Monitor `market_width`** - Narrow markets (low width) indicate more efficient pricing and more reliable EV calculations
419425
5. **Act quickly** - +EV opportunities are fleeting; lines move fast

content/en/api-reference/stream.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ A new positive expected value opportunity has been found. Only sent on `opportun
150150
```
151151
event: ev:detected
152152
id: evt_00043
153-
data: [{"id":"a1b2c3d4e5f6","game_id":"nba_phosuns_phi76ers_2026-02-08","ev_percentage":4.35,"odds_american":-105,"odds_decimal":1.952,"no_vig_odds":-101,"selection":"PHO Suns -3.5","market":"point_spread","line":-3.5,"sportsbook":"draftkings","game":"PHO Suns @ PHI 76ers","sport":"basketball","league":"nba","home_team":"PHI 76ers","away_team":"PHO Suns","start_time":"2026-02-08T19:00:00.000Z","is_live":false,"confidence_score":72,"kelly_percent":0.038,"book_count":4,"detected_at":"2026-02-08T18:47:20.000Z"}]
153+
data: [{"id":"a1b2c3d4e5f6","game_id":"nba_phosuns_phi76ers_2026-02-08","ev_percentage":4.35,"odds_american":-105,"odds_decimal":1.952,"no_vig_odds":-101,"selection":"PHO Suns -3.5","market":"point_spread","line":-3.5,"sportsbook":"draftkings","game":"PHO Suns @ PHI 76ers","sport":"basketball","league":"nba","home_team":"PHI 76ers","away_team":"PHO Suns","start_time":"2026-02-08T19:00:00.000Z","is_live":false,"confidence_score":72,"kelly_percent":3.8,"book_count":4,"detected_at":"2026-02-08T18:47:20.000Z"}]
154154
```
155155

156156
### `ev:expired`

content/en/api-reference/websocket.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ Snapshot of opportunities for a single channel type. Sent once per subscribed op
213213
"start_time": "2026-02-08T19:00:00.000Z",
214214
"is_live": false,
215215
"confidence_score": 72,
216-
"kelly_percent": 0.038,
216+
"kelly_percent": 3.8,
217217
"book_count": 4,
218218
"detected_at": "2026-02-08T18:47:20.000Z"
219219
}
@@ -326,7 +326,7 @@ New +EV opportunity found. Pro tier or higher only.
326326
"start_time": "2026-02-08T19:00:00.000Z",
327327
"is_live": false,
328328
"confidence_score": 72,
329-
"kelly_percent": 0.038,
329+
"kelly_percent": 3.8,
330330
"book_count": 4,
331331
"detected_at": "2026-02-08T18:47:20.000Z"
332332
}

content/en/examples/value-betting.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async function sendAlert(opp) {
7070
{ name: 'Book', value: opp.sportsbook, inline: true },
7171
{ name: 'Odds', value: String(opp.odds_american), inline: true },
7272
{ name: 'EV', value: `+${opp.ev_percentage}%`, inline: true },
73-
{ name: 'Kelly', value: `${opp.kelly_percent}`, inline: true },
73+
{ name: 'Kelly', value: `${opp.kelly_percent.toFixed(2)}%`, inline: true },
7474
{ name: 'Devig', value: `${opp.no_vig_odds} (${opp.sharp_book})`, inline: true },
7575
)
7676
.setTimestamp();
@@ -130,7 +130,7 @@ Selection: ${opp.selection}
130130
Book: ${opp.sportsbook}
131131
Odds: ${opp.odds_american}
132132
EV: +${opp.ev_percentage}%
133-
Kelly: ${opp.kelly_percent}
133+
Kelly: ${opp.kelly_percent.toFixed(2)}%
134134
`.trim();
135135

136136
await bot.sendMessage(chatId, message, { parse_mode: 'Markdown' });

content/en/quickstart.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ Response:
157157
"odds_probability": 0.535
158158
},
159159
"ev_percentage": 4.35,
160-
"kelly_percent": 0.0218
160+
"kelly_percent": 2.18
161161
}
162162
],
163163
"meta": {

content/en/sdks/python.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ evs = client.ev.get(min_ev=3.0, sport="basketball")
4141
for opp in evs.data:
4242
print(f"+{opp.ev_percentage:.1f}% EV on {opp.selection} @ {opp.sportsbook}")
4343
if opp.kelly_percent:
44-
print(f" Kelly: {opp.kelly_percent:.1%}, Confidence: {opp.confidence_score}")
44+
print(f" Kelly: {opp.kelly_percent:.2f}%, Confidence: {opp.confidence_score}")
4545

4646
# Best odds across sportsbooks
4747
odds = client.odds.best(league="nba", market="moneyline")
@@ -83,7 +83,7 @@ for opp in evs.data:
8383
print(f"+{opp.ev_percentage:.1f}% on {opp.selection} @ {opp.sportsbook}")
8484
print(f" Fair probability: {opp.fair_probability}")
8585
print(f" Devig: {opp.devig_method} via {opp.sharp_book}")
86-
print(f" Kelly: {opp.kelly_percent:.1%}")
86+
print(f" Kelly: {opp.kelly_percent:.2f}%")
8787
print(f" Confidence: {opp.confidence_score}/100")
8888
```
8989

0 commit comments

Comments
 (0)