kellyiq 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kellyiq-0.0.1/LICENSE +21 -0
- kellyiq-0.0.1/PKG-INFO +82 -0
- kellyiq-0.0.1/README.md +62 -0
- kellyiq-0.0.1/pyproject.toml +29 -0
- kellyiq-0.0.1/setup.cfg +4 -0
- kellyiq-0.0.1/src/kellyiq/__init__.py +25 -0
- kellyiq-0.0.1/src/kellyiq/_client.py +117 -0
- kellyiq-0.0.1/src/kellyiq.egg-info/PKG-INFO +82 -0
- kellyiq-0.0.1/src/kellyiq.egg-info/SOURCES.txt +9 -0
- kellyiq-0.0.1/src/kellyiq.egg-info/dependency_links.txt +1 -0
- kellyiq-0.0.1/src/kellyiq.egg-info/top_level.txt +1 -0
kellyiq-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 KellyIQ LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
kellyiq-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kellyiq
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Size a whole sports betting slate as one portfolio: a client for the KellyIQ API.
|
|
5
|
+
Author-email: KellyIQ <opensource@kellyiq.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://www.kellyiq.com/developers
|
|
8
|
+
Project-URL: Documentation, https://www.kellyiq.com/developers/quickstart
|
|
9
|
+
Project-URL: Source, https://github.com/kellyiq/kellyiq-python
|
|
10
|
+
Keywords: kelly criterion,bet sizing,sports betting,portfolio optimization,bankroll
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# kellyiq
|
|
22
|
+
|
|
23
|
+
Size a whole sports betting slate as one portfolio, from Python.
|
|
24
|
+
|
|
25
|
+
A small client for the [KellyIQ API](https://www.kellyiq.com/developers): send your bankroll and the
|
|
26
|
+
bets you have chosen, with your view of each, and get back a stake for every bet, sized across the
|
|
27
|
+
slate together under per-bet and total-exposure limits. No dependencies.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install kellyiq
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from kellyiq import KellyIQ
|
|
35
|
+
|
|
36
|
+
client = KellyIQ() # reads KELLYIQ_API_KEY
|
|
37
|
+
|
|
38
|
+
result = client.optimize({
|
|
39
|
+
"bankroll": 1000,
|
|
40
|
+
"picks": [
|
|
41
|
+
{"sport": "baseball_mlb", "team": "Dodgers", "opponent": "Padres",
|
|
42
|
+
"odds": -145, "opponentOdds": 125, "confidence": "STRONG_EDGE"},
|
|
43
|
+
{"sport": "baseball_mlb", "team": "Cubs", "opponent": "Cardinals",
|
|
44
|
+
"odds": 120, "opponentOdds": -140, "confidence": "MODERATE_EDGE"},
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
for stake in result["allocation"]["stakes"]:
|
|
49
|
+
print(f'{stake["team"]}: ${stake["stake"]}')
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Create an API key in the KellyIQ app under **Settings → API**. Every plan includes API access, Free
|
|
53
|
+
included, each with its own daily allowance.
|
|
54
|
+
|
|
55
|
+
## What's in it
|
|
56
|
+
|
|
57
|
+
| Method | Endpoint | Counts against the allowance |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `optimize(slate)` | `POST /v1/optimize` | Yes |
|
|
60
|
+
| `sports()` | `GET /v1/sports` | No |
|
|
61
|
+
| `me()` | `GET /v1/me` | No |
|
|
62
|
+
| `devig(markets)` | `POST /v1/devig` | No |
|
|
63
|
+
| `implied_probability(odds)` | `POST /v1/implied-probability` | No |
|
|
64
|
+
| `kelly(bets, bankroll=None, fraction=None)` | `POST /v1/kelly` | No |
|
|
65
|
+
|
|
66
|
+
Responses are the API's JSON as Python dicts; the [API reference](https://www.kellyiq.com/developers/reference/optimize)
|
|
67
|
+
describes every field. Errors raise `KellyIQError` with `status`, `code`, `violations`, `request_id`
|
|
68
|
+
and `retry_after`.
|
|
69
|
+
|
|
70
|
+
This is an early release. A fuller SDK (typed results, automatic retries) is on the way; the
|
|
71
|
+
methods above will keep working.
|
|
72
|
+
|
|
73
|
+
## More
|
|
74
|
+
|
|
75
|
+
- [Quickstart](https://www.kellyiq.com/developers/quickstart) and [docs](https://www.kellyiq.com/developers)
|
|
76
|
+
- [Examples and AI-assistant (MCP) configs](https://github.com/kellyiq/kellyiq-examples)
|
|
77
|
+
- Support: support@kellyiq.com
|
|
78
|
+
|
|
79
|
+
KellyIQ sizes the bets you choose; it does not choose bets or predict outcomes. Results are
|
|
80
|
+
calculations, not betting advice. 21+, where legal.
|
|
81
|
+
|
|
82
|
+
MIT licensed.
|
kellyiq-0.0.1/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# kellyiq
|
|
2
|
+
|
|
3
|
+
Size a whole sports betting slate as one portfolio, from Python.
|
|
4
|
+
|
|
5
|
+
A small client for the [KellyIQ API](https://www.kellyiq.com/developers): send your bankroll and the
|
|
6
|
+
bets you have chosen, with your view of each, and get back a stake for every bet, sized across the
|
|
7
|
+
slate together under per-bet and total-exposure limits. No dependencies.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install kellyiq
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from kellyiq import KellyIQ
|
|
15
|
+
|
|
16
|
+
client = KellyIQ() # reads KELLYIQ_API_KEY
|
|
17
|
+
|
|
18
|
+
result = client.optimize({
|
|
19
|
+
"bankroll": 1000,
|
|
20
|
+
"picks": [
|
|
21
|
+
{"sport": "baseball_mlb", "team": "Dodgers", "opponent": "Padres",
|
|
22
|
+
"odds": -145, "opponentOdds": 125, "confidence": "STRONG_EDGE"},
|
|
23
|
+
{"sport": "baseball_mlb", "team": "Cubs", "opponent": "Cardinals",
|
|
24
|
+
"odds": 120, "opponentOdds": -140, "confidence": "MODERATE_EDGE"},
|
|
25
|
+
],
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
for stake in result["allocation"]["stakes"]:
|
|
29
|
+
print(f'{stake["team"]}: ${stake["stake"]}')
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Create an API key in the KellyIQ app under **Settings → API**. Every plan includes API access, Free
|
|
33
|
+
included, each with its own daily allowance.
|
|
34
|
+
|
|
35
|
+
## What's in it
|
|
36
|
+
|
|
37
|
+
| Method | Endpoint | Counts against the allowance |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `optimize(slate)` | `POST /v1/optimize` | Yes |
|
|
40
|
+
| `sports()` | `GET /v1/sports` | No |
|
|
41
|
+
| `me()` | `GET /v1/me` | No |
|
|
42
|
+
| `devig(markets)` | `POST /v1/devig` | No |
|
|
43
|
+
| `implied_probability(odds)` | `POST /v1/implied-probability` | No |
|
|
44
|
+
| `kelly(bets, bankroll=None, fraction=None)` | `POST /v1/kelly` | No |
|
|
45
|
+
|
|
46
|
+
Responses are the API's JSON as Python dicts; the [API reference](https://www.kellyiq.com/developers/reference/optimize)
|
|
47
|
+
describes every field. Errors raise `KellyIQError` with `status`, `code`, `violations`, `request_id`
|
|
48
|
+
and `retry_after`.
|
|
49
|
+
|
|
50
|
+
This is an early release. A fuller SDK (typed results, automatic retries) is on the way; the
|
|
51
|
+
methods above will keep working.
|
|
52
|
+
|
|
53
|
+
## More
|
|
54
|
+
|
|
55
|
+
- [Quickstart](https://www.kellyiq.com/developers/quickstart) and [docs](https://www.kellyiq.com/developers)
|
|
56
|
+
- [Examples and AI-assistant (MCP) configs](https://github.com/kellyiq/kellyiq-examples)
|
|
57
|
+
- Support: support@kellyiq.com
|
|
58
|
+
|
|
59
|
+
KellyIQ sizes the bets you choose; it does not choose bets or predict outcomes. Results are
|
|
60
|
+
calculations, not betting advice. 21+, where legal.
|
|
61
|
+
|
|
62
|
+
MIT licensed.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kellyiq"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Size a whole sports betting slate as one portfolio: a client for the KellyIQ API."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "KellyIQ", email = "opensource@kellyiq.com" }]
|
|
13
|
+
keywords = ["kelly criterion", "bet sizing", "sports betting", "portfolio optimization", "bankroll"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
20
|
+
]
|
|
21
|
+
dependencies = []
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://www.kellyiq.com/developers"
|
|
25
|
+
Documentation = "https://www.kellyiq.com/developers/quickstart"
|
|
26
|
+
Source = "https://github.com/kellyiq/kellyiq-python"
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
where = ["src"]
|
kellyiq-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""KellyIQ: size a whole sports betting slate as one portfolio.
|
|
2
|
+
|
|
3
|
+
A small client for the KellyIQ API (https://www.kellyiq.com/developers). No dependencies.
|
|
4
|
+
|
|
5
|
+
from kellyiq import KellyIQ
|
|
6
|
+
|
|
7
|
+
client = KellyIQ() # reads KELLYIQ_API_KEY
|
|
8
|
+
result = client.optimize({
|
|
9
|
+
"bankroll": 1000,
|
|
10
|
+
"picks": [
|
|
11
|
+
{"sport": "baseball_mlb", "team": "Dodgers", "opponent": "Padres",
|
|
12
|
+
"odds": -145, "opponentOdds": 125, "confidence": "STRONG_EDGE"},
|
|
13
|
+
],
|
|
14
|
+
})
|
|
15
|
+
for stake in result["allocation"]["stakes"]:
|
|
16
|
+
print(stake["team"], stake["stake"])
|
|
17
|
+
|
|
18
|
+
KellyIQ sizes the bets you choose; it does not choose bets or predict outcomes. Results are
|
|
19
|
+
calculations, not betting advice.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from ._client import KellyIQ, KellyIQError
|
|
23
|
+
|
|
24
|
+
__all__ = ["KellyIQ", "KellyIQError", "__version__"]
|
|
25
|
+
__version__ = "0.0.1"
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import urllib.error
|
|
4
|
+
import urllib.request
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
DEFAULT_BASE_URL = "https://api.kellyiq.com"
|
|
8
|
+
_VERSION = "0.0.1"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class KellyIQError(Exception):
|
|
12
|
+
"""An error response from the KellyIQ API.
|
|
13
|
+
|
|
14
|
+
Attributes mirror the API's error body: ``status`` (HTTP status), ``code`` (for example
|
|
15
|
+
``VALIDATION_ERROR`` or ``DAILY_LIMIT_EXCEEDED``), ``violations`` (which field was wrong, for
|
|
16
|
+
validation errors), ``request_id`` (quote it to support@kellyiq.com) and ``retry_after``
|
|
17
|
+
(seconds, when the API asks you to wait).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, status: int, code: str, message: str, violations: Optional[List[str]] = None,
|
|
21
|
+
request_id: Optional[str] = None, retry_after: Optional[int] = None,
|
|
22
|
+
body: Optional[Dict[str, Any]] = None) -> None:
|
|
23
|
+
detail = f"{code}: {message}" + (f" ({'; '.join(violations)})" if violations else "")
|
|
24
|
+
super().__init__(f"HTTP {status} {detail}")
|
|
25
|
+
self.status = status
|
|
26
|
+
self.code = code
|
|
27
|
+
self.message = message
|
|
28
|
+
self.violations = violations or []
|
|
29
|
+
self.request_id = request_id
|
|
30
|
+
self.retry_after = retry_after
|
|
31
|
+
self.body = body or {}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class KellyIQ:
|
|
35
|
+
"""Client for the KellyIQ API.
|
|
36
|
+
|
|
37
|
+
:param api_key: your API key (``kiqa_...``); defaults to the ``KELLYIQ_API_KEY`` environment variable.
|
|
38
|
+
Create one in the KellyIQ app under Settings, API. Every plan includes API access.
|
|
39
|
+
:param base_url: defaults to ``KELLYIQ_API_BASE`` or https://api.kellyiq.com.
|
|
40
|
+
:param timeout: seconds to wait for a response (an optimize can take several seconds).
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, api_key: Optional[str] = None, base_url: Optional[str] = None,
|
|
44
|
+
timeout: float = 60.0) -> None:
|
|
45
|
+
key = api_key or os.environ.get("KELLYIQ_API_KEY")
|
|
46
|
+
if not key:
|
|
47
|
+
raise ValueError("Pass api_key or set the KELLYIQ_API_KEY environment variable.")
|
|
48
|
+
self._key = key
|
|
49
|
+
self._base = (base_url or os.environ.get("KELLYIQ_API_BASE") or DEFAULT_BASE_URL).rstrip("/")
|
|
50
|
+
self._timeout = timeout
|
|
51
|
+
|
|
52
|
+
def __repr__(self) -> str: # never show the key
|
|
53
|
+
return f"KellyIQ(base_url={self._base!r})"
|
|
54
|
+
|
|
55
|
+
# ---- endpoints -------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
def optimize(self, slate: Dict[str, Any]) -> Dict[str, Any]:
|
|
58
|
+
"""Size a slate (POST /v1/optimize). Returns the API response; stakes are in
|
|
59
|
+
``result["allocation"]["stakes"]``. Counts against your daily optimize allowance."""
|
|
60
|
+
return self._request("POST", "/v1/optimize", slate)
|
|
61
|
+
|
|
62
|
+
def sports(self) -> List[Dict[str, Any]]:
|
|
63
|
+
"""The sports you can size right now, with the key to send as each pick's ``sport``. Free."""
|
|
64
|
+
return self._request("GET", "/v1/sports")["sports"]
|
|
65
|
+
|
|
66
|
+
def me(self) -> Dict[str, Any]:
|
|
67
|
+
"""Your plan, today's allowance and the settings optimize applies by default. Free."""
|
|
68
|
+
return self._request("GET", "/v1/me")
|
|
69
|
+
|
|
70
|
+
def devig(self, markets: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
71
|
+
"""Fair probabilities with the bookmaker's margin removed. Each market: ``{"odds": [-145, 125]}``."""
|
|
72
|
+
return self._request("POST", "/v1/devig", {"markets": markets})["results"]
|
|
73
|
+
|
|
74
|
+
def implied_probability(self, odds: List[int]) -> List[Dict[str, Any]]:
|
|
75
|
+
"""The probability each American price implies, margin included."""
|
|
76
|
+
return self._request("POST", "/v1/implied-probability", {"odds": odds})["results"]
|
|
77
|
+
|
|
78
|
+
def kelly(self, bets: List[Dict[str, Any]], bankroll: Optional[float] = None,
|
|
79
|
+
fraction: Optional[float] = None) -> List[Dict[str, Any]]:
|
|
80
|
+
"""Kelly stake for each bet on its own. For two or more bets placed together, use optimize()."""
|
|
81
|
+
body: Dict[str, Any] = {"bets": bets}
|
|
82
|
+
if bankroll is not None:
|
|
83
|
+
body["bankroll"] = bankroll
|
|
84
|
+
if fraction is not None:
|
|
85
|
+
body["fraction"] = fraction
|
|
86
|
+
return self._request("POST", "/v1/kelly", body)["results"]
|
|
87
|
+
|
|
88
|
+
# ---- transport -------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
def _request(self, method: str, path: str, body: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
91
|
+
data = json.dumps(body).encode("utf-8") if body is not None else None
|
|
92
|
+
req = urllib.request.Request(self._base + path, data=data, method=method)
|
|
93
|
+
req.add_header("Authorization", f"Bearer {self._key}")
|
|
94
|
+
req.add_header("Accept", "application/json")
|
|
95
|
+
# Identifies SDK traffic, so KellyIQ can tell how many calls come through this package.
|
|
96
|
+
req.add_header("User-Agent", f"kellyiq-python/{_VERSION}")
|
|
97
|
+
if data is not None:
|
|
98
|
+
req.add_header("Content-Type", "application/json")
|
|
99
|
+
try:
|
|
100
|
+
with urllib.request.urlopen(req, timeout=self._timeout) as resp:
|
|
101
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
102
|
+
except urllib.error.HTTPError as e:
|
|
103
|
+
raw = e.read().decode("utf-8", "replace")
|
|
104
|
+
try:
|
|
105
|
+
err = json.loads(raw)
|
|
106
|
+
except ValueError:
|
|
107
|
+
err = {}
|
|
108
|
+
retry = e.headers.get("Retry-After")
|
|
109
|
+
raise KellyIQError(
|
|
110
|
+
status=e.code,
|
|
111
|
+
code=err.get("error", f"HTTP_{e.code}"),
|
|
112
|
+
message=err.get("message", raw[:200] or e.reason),
|
|
113
|
+
violations=err.get("violations"),
|
|
114
|
+
request_id=err.get("requestId") or e.headers.get("X-Request-Id"),
|
|
115
|
+
retry_after=int(retry) if retry and retry.isdigit() else None,
|
|
116
|
+
body=err,
|
|
117
|
+
) from None
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kellyiq
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Size a whole sports betting slate as one portfolio: a client for the KellyIQ API.
|
|
5
|
+
Author-email: KellyIQ <opensource@kellyiq.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://www.kellyiq.com/developers
|
|
8
|
+
Project-URL: Documentation, https://www.kellyiq.com/developers/quickstart
|
|
9
|
+
Project-URL: Source, https://github.com/kellyiq/kellyiq-python
|
|
10
|
+
Keywords: kelly criterion,bet sizing,sports betting,portfolio optimization,bankroll
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# kellyiq
|
|
22
|
+
|
|
23
|
+
Size a whole sports betting slate as one portfolio, from Python.
|
|
24
|
+
|
|
25
|
+
A small client for the [KellyIQ API](https://www.kellyiq.com/developers): send your bankroll and the
|
|
26
|
+
bets you have chosen, with your view of each, and get back a stake for every bet, sized across the
|
|
27
|
+
slate together under per-bet and total-exposure limits. No dependencies.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install kellyiq
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from kellyiq import KellyIQ
|
|
35
|
+
|
|
36
|
+
client = KellyIQ() # reads KELLYIQ_API_KEY
|
|
37
|
+
|
|
38
|
+
result = client.optimize({
|
|
39
|
+
"bankroll": 1000,
|
|
40
|
+
"picks": [
|
|
41
|
+
{"sport": "baseball_mlb", "team": "Dodgers", "opponent": "Padres",
|
|
42
|
+
"odds": -145, "opponentOdds": 125, "confidence": "STRONG_EDGE"},
|
|
43
|
+
{"sport": "baseball_mlb", "team": "Cubs", "opponent": "Cardinals",
|
|
44
|
+
"odds": 120, "opponentOdds": -140, "confidence": "MODERATE_EDGE"},
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
for stake in result["allocation"]["stakes"]:
|
|
49
|
+
print(f'{stake["team"]}: ${stake["stake"]}')
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Create an API key in the KellyIQ app under **Settings → API**. Every plan includes API access, Free
|
|
53
|
+
included, each with its own daily allowance.
|
|
54
|
+
|
|
55
|
+
## What's in it
|
|
56
|
+
|
|
57
|
+
| Method | Endpoint | Counts against the allowance |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `optimize(slate)` | `POST /v1/optimize` | Yes |
|
|
60
|
+
| `sports()` | `GET /v1/sports` | No |
|
|
61
|
+
| `me()` | `GET /v1/me` | No |
|
|
62
|
+
| `devig(markets)` | `POST /v1/devig` | No |
|
|
63
|
+
| `implied_probability(odds)` | `POST /v1/implied-probability` | No |
|
|
64
|
+
| `kelly(bets, bankroll=None, fraction=None)` | `POST /v1/kelly` | No |
|
|
65
|
+
|
|
66
|
+
Responses are the API's JSON as Python dicts; the [API reference](https://www.kellyiq.com/developers/reference/optimize)
|
|
67
|
+
describes every field. Errors raise `KellyIQError` with `status`, `code`, `violations`, `request_id`
|
|
68
|
+
and `retry_after`.
|
|
69
|
+
|
|
70
|
+
This is an early release. A fuller SDK (typed results, automatic retries) is on the way; the
|
|
71
|
+
methods above will keep working.
|
|
72
|
+
|
|
73
|
+
## More
|
|
74
|
+
|
|
75
|
+
- [Quickstart](https://www.kellyiq.com/developers/quickstart) and [docs](https://www.kellyiq.com/developers)
|
|
76
|
+
- [Examples and AI-assistant (MCP) configs](https://github.com/kellyiq/kellyiq-examples)
|
|
77
|
+
- Support: support@kellyiq.com
|
|
78
|
+
|
|
79
|
+
KellyIQ sizes the bets you choose; it does not choose bets or predict outcomes. Results are
|
|
80
|
+
calculations, not betting advice. 21+, where legal.
|
|
81
|
+
|
|
82
|
+
MIT licensed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kellyiq
|