Skip to content

Commit c8f4cc5

Browse files
Mlaz-codeclaude
andcommitted
docs(low-hold): fix pricing tier, add SDK streaming examples
- Fix low_hold feature comparison: Hobby tier → Yes (matches code gate) - Fix Hobby card to list Arbitrage + Low Hold Detection - Add LowHoldOpportunity/LowHoldSide type definitions to TS SDK - Add low_hold:detected and low_hold:expired event handlers - Add lowHoldMap state management and reconnect cleanup - Update staleness docs to mention low-hold Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e7574df commit c8f4cc5

2 files changed

Lines changed: 64 additions & 6 deletions

File tree

content/pricing.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ Choose the plan that fits your betting strategy. All plans include access to our
4242
- No streaming*
4343
- Real-time data
4444
- 5 sportsbooks
45-
- Odds and Schedule
45+
- Odds, Schedule, Arbitrage
46+
- Low Hold Detection
4647

4748
Get Started
4849
</Cards.Card>
@@ -115,7 +116,7 @@ Need higher limits, custom features, or an SLA? Contact us for enterprise pricin
115116
| `GET /opportunities/ev` | No | No | Yes | Yes | Yes |
116117
| `GET /opportunities/arbitrage` | No | Yes | Yes | Yes | Yes |
117118
| `GET /opportunities/middles` | No | No | Yes | Yes | Yes |
118-
| `GET /opportunities/low_hold` | No | No | Yes | Yes | Yes |
119+
| `GET /opportunities/low_hold` | No | Yes | Yes | Yes | Yes |
119120
| CSV Export | No | No | Yes | Yes | Yes |
120121
| SSE Streaming | No | +$99/mo | +$99/mo | +$99/mo | Included |
121122
| Priority Support | No | No | No | Yes | Yes |

content/sdks/typescript.mdx

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,46 @@ interface ArbOpportunity {
107107
detected_at: string;
108108
}
109109

110+
interface LowHoldOpportunity {
111+
id: string;
112+
event_id: string;
113+
event_name: string;
114+
sport: string;
115+
league: string;
116+
market_type: string;
117+
line: number | null;
118+
hold_percentage: number;
119+
side1: LowHoldSide;
120+
side2: LowHoldSide;
121+
side3: LowHoldSide | null; // 3-way markets (soccer, hockey)
122+
is_live: boolean;
123+
is_alternate_line: boolean;
124+
all_books: string[];
125+
confidence: number;
126+
odds_age_seconds: number;
127+
possibly_stale: boolean;
128+
detected_at: string;
129+
}
130+
131+
interface LowHoldSide {
132+
selection: string;
133+
books: string[];
134+
line: number | null;
135+
odds: {
136+
american: number;
137+
decimal: number;
138+
implied_probability: number;
139+
fair_probability: number;
140+
};
141+
deep_links: Record<string, string>;
142+
}
143+
110144
// ─── State Management ─────────────────────────────────────────────────────
111145
// Keyed by odds line ID (e.g. "draftkings_33483153_moneyline_PHO")
112146
const oddsMap = new Map<string, OddsLine>();
113147
const evMap = new Map<string, EVOpportunity>();
114148
const arbMap = new Map<string, ArbOpportunity>();
149+
const lowHoldMap = new Map<string, LowHoldOpportunity>();
115150
let isReady = false;
116151

117152
// ─── Connect ──────────────────────────────────────────────────────────────
@@ -132,6 +167,7 @@ eventSource.addEventListener('connected', (e) => {
132167
oddsMap.clear();
133168
evMap.clear();
134169
arbMap.clear();
170+
lowHoldMap.clear();
135171
isReady = false;
136172
}
137173
});
@@ -143,8 +179,7 @@ eventSource.addEventListener('snapshot', (e) => {
143179
// Odds snapshots: keyed by sportsbook name
144180
// e.g. { "draftkings": [OddsLine, ...], "fanduel": [OddsLine, ...] }
145181
for (const [key, value] of Object.entries(data)) {
146-
if (Array.isArray(value) && key !== 'ev' && key !== 'arbitrage'
147-
&& key !== 'middles' && key !== 'low_hold') {
182+
if (Array.isArray(value) && !['ev', 'arbitrage', 'middles', 'low_hold'].includes(key)) {
148183
// Odds data — key is sportsbook name
149184
for (const odds of value as OddsLine[]) {
150185
oddsMap.set(odds.id, odds);
@@ -163,13 +198,18 @@ eventSource.addEventListener('snapshot', (e) => {
163198
arbMap.set(arb.id, arb);
164199
}
165200
}
201+
if (data.low_hold) {
202+
for (const lh of data.low_hold as LowHoldOpportunity[]) {
203+
lowHoldMap.set(lh.id, lh);
204+
}
205+
}
166206
});
167207

168208
eventSource.addEventListener('snapshot:complete', (e) => {
169209
const { books, total_odds } = JSON.parse(e.data);
170210
isReady = true;
171211
console.log(`Ready: ${total_odds} odds from ${books.join(', ')}`);
172-
console.log(`${evMap.size} EV opportunities, ${arbMap.size} arb opportunities`);
212+
console.log(`${evMap.size} EV, ${arbMap.size} arb, ${lowHoldMap.size} low-hold opportunities`);
173213
});
174214

175215
// ─── Real-time odds updates (DELTAS — merge into local state) ─────────────
@@ -225,6 +265,22 @@ eventSource.addEventListener('arb:expired', (e) => {
225265
}
226266
});
227267

268+
eventSource.addEventListener('low_hold:detected', (e) => {
269+
const opps = JSON.parse(e.data) as LowHoldOpportunity[];
270+
for (const opp of opps) {
271+
if (opp.possibly_stale) continue;
272+
lowHoldMap.set(opp.id, opp);
273+
console.log(`Low Hold: ${opp.hold_percentage}% — ${opp.event_name} (${opp.market_type})`);
274+
}
275+
});
276+
277+
eventSource.addEventListener('low_hold:expired', (e) => {
278+
const { expired } = JSON.parse(e.data) as { expired: string[] };
279+
for (const id of expired) {
280+
lowHoldMap.delete(id);
281+
}
282+
});
283+
228284
// ─── Health monitoring ────────────────────────────────────────────────────
229285
let lastHeartbeat = Date.now();
230286

@@ -306,7 +362,7 @@ function americanToProbability(american: number): number {
306362

307363
## Staleness Metadata
308364

309-
EV and arbitrage opportunity responses include staleness information to help you filter out opportunities based on stale odds:
365+
EV, arbitrage, and low-hold opportunity responses include staleness information to help you filter out opportunities based on stale odds:
310366

311367
```typescript
312368
interface EVOpportunity {
@@ -345,6 +401,7 @@ eventSource.addEventListener('connected', (e) => {
345401
oddsMap.clear();
346402
evMap.clear();
347403
arbMap.clear();
404+
lowHoldMap.clear();
348405
isReady = false;
349406
}
350407
});

0 commit comments

Comments
 (0)