Skip to content

Commit 5b3995b

Browse files
Mlaz-codeclaude
andcommitted
Initial release — sharpapi v0.1.0
Sync + async clients, 20 Pydantic v2 models, SSE streaming, pandas DataFrame integration, 88 tests. Published to PyPI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
0 parents  commit 5b3995b

21 files changed

Lines changed: 3616 additions & 0 deletions

.github/workflows/publish.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
publish:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
id-token: write
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- uses: actions/setup-python@v5
17+
with:
18+
python-version: "3.12"
19+
20+
- name: Install build tools
21+
run: pip install hatch
22+
23+
- name: Build
24+
run: hatch build
25+
26+
- name: Publish to PyPI
27+
uses: pypa/gh-action-pypi-publish@release/v1

.github/workflows/test.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- uses: actions/setup-python@v5
20+
with:
21+
python-version: ${{ matrix.python-version }}
22+
23+
- name: Install dependencies
24+
run: |
25+
pip install -e ".[test,pandas]"
26+
27+
- name: Run tests
28+
run: pytest -v

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
__pycache__/
2+
*.py[cod]
3+
*.so
4+
*.egg-info/
5+
dist/
6+
build/
7+
.venv/
8+
.env
9+
.pytest_cache/
10+
.ruff_cache/
11+
*.egg
12+
.mypy_cache/

README.md

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# SharpAPI Python SDK
2+
3+
Official Python client for the [SharpAPI](https://sharpapi.io) real-time sports betting odds API.
4+
5+
Get pre-computed +EV opportunities, arbitrage detection, middles, and live odds from 20+ sportsbooks — with Pinnacle as the sharp reference.
6+
7+
## Install
8+
9+
```bash
10+
pip install sharpapi
11+
```
12+
13+
## Quick Start
14+
15+
```python
16+
from sharpapi import SharpAPI
17+
18+
client = SharpAPI("sk_live_xxx")
19+
20+
# --- Arbitrage opportunities ---
21+
arbs = client.arbitrage.get(min_profit=1.0, league="nba")
22+
for arb in arbs.data:
23+
print(f"{arb.profit_percent:.2f}% profit — {arb.event_name}")
24+
for leg in arb.legs:
25+
print(f" {leg.sportsbook}: {leg.selection} @ {leg.odds_american} ({leg.stake_percent:.1f}%)")
26+
27+
# --- +EV opportunities ---
28+
evs = client.ev.get(min_ev=3.0, sport="basketball")
29+
for opp in evs.data:
30+
print(f"+{opp.ev_percent:.1f}% EV on {opp.selection} @ {opp.sportsbook}")
31+
if opp.kelly_fraction:
32+
print(f" Kelly: {opp.kelly_fraction:.1%} of bankroll")
33+
34+
# --- Best odds across books ---
35+
odds = client.odds.best(league="nba", market="moneyline")
36+
for line in odds.data:
37+
print(f"{line.home_team} vs {line.away_team}: {line.selection} {line.odds_american}")
38+
```
39+
40+
## Streaming
41+
42+
Real-time SSE streaming for odds updates and opportunity alerts (requires WebSocket add-on):
43+
44+
```python
45+
stream = client.stream.opportunities(league="nba")
46+
47+
@stream.on("ev:detected")
48+
def on_ev(data):
49+
for opp in data:
50+
print(f"+EV: {opp['selection']} {opp['ev_percent']}% @ {opp['sportsbook']}")
51+
52+
@stream.on("arb:detected")
53+
def on_arb(data):
54+
for arb in data:
55+
print(f"Arb: {arb['profit_percent']}% — {arb['event_name']}")
56+
57+
stream.connect() # Blocks, processing events
58+
```
59+
60+
Or iterate over events:
61+
62+
```python
63+
for event_type, data in stream.iter_events():
64+
if event_type == "ev:detected":
65+
print(data)
66+
```
67+
68+
## All Resources
69+
70+
```python
71+
# Odds
72+
client.odds.get(sport="basketball", league="nba")
73+
client.odds.best(league="nfl", market="moneyline")
74+
client.odds.comparison(event_id="abc123")
75+
client.odds.batch(event_ids=["abc123", "def456"])
76+
77+
# Opportunities
78+
client.ev.get(min_ev=2.0, sportsbook="draftkings")
79+
client.arbitrage.get(min_profit=0.5, sport="football")
80+
client.middles.get(sport="football", min_size=3.0)
81+
client.low_hold.get(max_hold=2.0)
82+
83+
# Reference data
84+
client.sports.list()
85+
client.leagues.list(sport="basketball")
86+
client.sportsbooks.list()
87+
client.events.list(league="nba", live=True)
88+
client.events.search("Lakers")
89+
90+
# Account
91+
client.account.me() # Tier, limits, features
92+
client.account.usage() # Request counts
93+
94+
# Streaming
95+
client.stream.odds(league="nba")
96+
client.stream.opportunities(min_ev=3.0)
97+
client.stream.all(sport="basketball")
98+
client.stream.event("event_id_here")
99+
```
100+
101+
## Data Quality
102+
103+
Every opportunity response includes staleness metadata to avoid acting on stale odds:
104+
105+
```python
106+
arbs = client.arbitrage.get()
107+
for arb in arbs.data:
108+
if arb.possibly_stale:
109+
print(f" Skipping — odds may be stale ({arb.oldest_odds_age_seconds}s old)")
110+
continue
111+
if "LIVE_HIGH_PROFIT_SUSPICIOUS" in arb.warnings:
112+
print(f" Skipping — likely phantom arb")
113+
continue
114+
print(f"Actionable: {arb.profit_percent}%")
115+
```
116+
117+
## Rate Limits
118+
119+
Rate limit info is available after every request:
120+
121+
```python
122+
response = client.odds.get()
123+
print(f"Remaining: {client.rate_limit.remaining}/{client.rate_limit.limit}")
124+
print(f"Tier: {client.rate_limit.tier}")
125+
```
126+
127+
## Error Handling
128+
129+
```python
130+
from sharpapi import (
131+
SharpAPI,
132+
AuthenticationError,
133+
TierRestrictedError,
134+
RateLimitedError,
135+
)
136+
137+
client = SharpAPI("sk_live_xxx")
138+
139+
try:
140+
evs = client.ev.get()
141+
except AuthenticationError:
142+
print("Invalid API key")
143+
except TierRestrictedError as e:
144+
print(f"Upgrade to {e.required_tier} tier for this feature")
145+
except RateLimitedError as e:
146+
print(f"Rate limited — retry after {e.retry_after}s")
147+
```
148+
149+
## Odds Conversion Utilities
150+
151+
```python
152+
from sharpapi import american_to_decimal, american_to_probability, decimal_to_american
153+
154+
american_to_decimal(-110) # 1.909
155+
american_to_decimal(150) # 2.5
156+
american_to_probability(-110) # 0.524
157+
decimal_to_american(2.5) # 150
158+
```
159+
160+
## Requirements
161+
162+
- Python 3.9+
163+
- httpx
164+
- pydantic v2
165+
166+
## Links
167+
168+
- [API Docs](https://docs.sharpapi.io)
169+
- [Dashboard](https://sharpapi.io/dashboard)
170+
- [Discord](https://discord.gg/sharpapi)

pyproject.toml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "sharpapi"
7+
version = "0.1.0"
8+
description = "Official Python SDK for the SharpAPI real-time sports betting odds API"
9+
readme = "README.md"
10+
license = "MIT"
11+
requires-python = ">=3.9"
12+
authors = [{ name = "SharpAPI", email = "support@sharpapi.io" }]
13+
keywords = ["sports-betting", "odds", "arbitrage", "ev", "api", "real-time", "pinnacle"]
14+
classifiers = [
15+
"Development Status :: 4 - Beta",
16+
"Intended Audience :: Developers",
17+
"License :: OSI Approved :: MIT License",
18+
"Programming Language :: Python :: 3",
19+
"Programming Language :: Python :: 3.9",
20+
"Programming Language :: Python :: 3.10",
21+
"Programming Language :: Python :: 3.11",
22+
"Programming Language :: Python :: 3.12",
23+
"Programming Language :: Python :: 3.13",
24+
"Topic :: Software Development :: Libraries :: Python Modules",
25+
"Typing :: Typed",
26+
]
27+
dependencies = [
28+
"httpx>=0.25.0",
29+
"pydantic>=2.0.0",
30+
]
31+
32+
[project.optional-dependencies]
33+
pandas = ["pandas>=1.5.0"]
34+
test = ["pytest>=8.0", "pytest-asyncio>=0.23", "respx>=0.21"]
35+
36+
[project.urls]
37+
Homepage = "https://sharpapi.io"
38+
Documentation = "https://docs.sharpapi.io/sdks/python"
39+
Repository = "https://github.com/Sharp-API/sharpapi-python"
40+
Changelog = "https://github.com/Sharp-API/sharpapi-python/releases"
41+
42+
[tool.hatch.build.targets.wheel]
43+
packages = ["src/sharpapi"]
44+
45+
[tool.ruff]
46+
target-version = "py39"
47+
line-length = 100
48+
49+
[tool.ruff.lint]
50+
select = ["E", "F", "I", "UP"]
51+
52+
[tool.pytest.ini_options]
53+
asyncio_mode = "auto"
54+
testpaths = ["tests"]
55+
56+
[tool.pyright]
57+
pythonVersion = "3.9"
58+
typeCheckingMode = "standard"

src/sharpapi/__init__.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""SharpAPI Python SDK — Real-time sports betting odds, +EV, and arbitrage detection.
2+
3+
Example::
4+
5+
from sharpapi import SharpAPI
6+
7+
client = SharpAPI("sk_live_xxx")
8+
9+
# Arbitrage opportunities
10+
arbs = client.arbitrage.get(min_profit=1.0)
11+
for arb in arbs.data:
12+
print(f"{arb.profit_percent}% — {arb.event_name}")
13+
14+
# +EV opportunities
15+
evs = client.ev.get(min_ev=3.0, league="nba")
16+
for opp in evs.data:
17+
print(f"+{opp.ev_percent}% on {opp.selection} @ {opp.sportsbook}")
18+
"""
19+
20+
from .async_client import AsyncSharpAPI
21+
from .client import SharpAPI
22+
from .exceptions import (
23+
AuthenticationError,
24+
RateLimitedError,
25+
SharpAPIError,
26+
StreamError,
27+
TierRestrictedError,
28+
ValidationError,
29+
)
30+
from .models import (
31+
APIResponse,
32+
AccountInfo,
33+
ArbitrageLeg,
34+
ArbitrageOpportunity,
35+
EVOpportunity,
36+
Event,
37+
GameState,
38+
League,
39+
LowHoldOpportunity,
40+
LowHoldSide,
41+
MiddleOpportunity,
42+
MiddleSide,
43+
OddsLine,
44+
OddsValue,
45+
Pagination,
46+
RateLimitInfo,
47+
ResponseMeta,
48+
Sport,
49+
Sportsbook,
50+
)
51+
from .streaming import EventStream
52+
from ._utils import american_to_decimal, american_to_probability, decimal_to_american
53+
54+
__version__ = "0.1.0"
55+
56+
__all__ = [
57+
# Clients
58+
"SharpAPI",
59+
"AsyncSharpAPI",
60+
# Models
61+
"APIResponse",
62+
"AccountInfo",
63+
"ArbitrageLeg",
64+
"ArbitrageOpportunity",
65+
"EVOpportunity",
66+
"Event",
67+
"GameState",
68+
"League",
69+
"LowHoldOpportunity",
70+
"LowHoldSide",
71+
"MiddleOpportunity",
72+
"MiddleSide",
73+
"OddsLine",
74+
"OddsValue",
75+
"Pagination",
76+
"RateLimitInfo",
77+
"ResponseMeta",
78+
"Sport",
79+
"Sportsbook",
80+
# Streaming
81+
"EventStream",
82+
# Exceptions
83+
"AuthenticationError",
84+
"RateLimitedError",
85+
"SharpAPIError",
86+
"StreamError",
87+
"TierRestrictedError",
88+
"ValidationError",
89+
# Utilities
90+
"american_to_decimal",
91+
"american_to_probability",
92+
"decimal_to_american",
93+
]

0 commit comments

Comments
 (0)