feat: add optional team_side + market_segment to odds/EV/closing models (#76, #689) - #13
Merged
Merged
Conversation
…ls (#76, #689)
OddsLine, EVOpportunity, ClosingOddsLine each gain optional team_side
("home"|"away"|"draw") + market_segment ("full_game","1st_half",...). Surfaces
the structured axes the API now emits (sharp-api-go #76 Phase C / #689).
Additive, default None. Bump 0.4.0 → 0.4.1.
Type: feat
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mlaz-code
force-pushed
the
feat/team-side-market-segment-76
branch
from
June 3, 2026 02:41
362b102 to
3a33100
Compare
Mlaz-code
added a commit
that referenced
this pull request
Jul 28, 2026
…ality_tier (#20) Fixes #18. ## The two defects, reproduced against live payloads ``` MiddleOpportunity.model_validate(<live row>) -> 2 validation errors side1.odds Field required side2.odds Field required EVOpportunity.model_validate(<live row>) -> parsed, but: wire confidence=80 -> model.confidence_score=None wire quality_tier='A' -> model has no such attribute ``` `client.middles()` could not parse **any** successful response. `confidence_score` read `None` on every row. ## One correction to the issue #18 says the wire sends `sportsbook` rather than `book`. **It doesn't** — live sides send `book`, which the model already accepted. The only field that broke parsing was the nested `odds`. Changing `book` on that report would have introduced a bug rather than fixed one. Live side shape, all 12 fields present on 4/4 sampled sides: ``` book selection line odds_american odds_decimal odds_probability fair_probability stake_percent odds_age_seconds external_event_id market_id selection_id ``` ## Choices worth flagging **`confidence_score` is aliased, not renamed.** `AliasChoices("confidence", "confidence_score")` matches the file's existing idiom (`fair_probability`/`true_probability`, `sharp_book`/`devig_book`) and keeps the public attribute name, so nobody's code breaks. **`MiddleSide` is a hard shape change** — `side.odds.american` becomes `side.odds_american`. Normally breaking; here it cannot be, because the model never parsed a response, so no working consumer exists to break. ## Bonus defect found while checking the release path `pyproject.toml` = `0.4.1`, `__init__.py` = `0.4.0`. #13 bumped one and missed the other, and **0.4.1 is already on PyPI** — so the published package reports `sharpapi.__version__ == "0.4.0"`. PyPI is immutable, so that stays wrong for 0.4.1 forever; the test here only stops the next one. Synced to 0.4.1 (matching what is published), which is a correction, not a release bump. ## Tests — 9 new, all against captured live payloads `tests/test_wire_contract.py` + `tests/fixtures/{middles,ev}_live.json`. Hand-written dicts could never have caught this: they'd be written from the same wrong belief as the model. Two notes on what is deliberately **not** asserted, both discovered by writing the check and watching it misfire: - **"every declared field is populated by some payload"** — dropped. A 2-row fixture cannot tell "the SDK declares a field the wire never sends" from "this sample lacked a conditional field." It flagged `sharp_odds_american`, `is_suspended` and the `*_ref` objects, all legitimately conditional. - **`importlib.metadata.version()`** for the version check — replaced with a direct read of `pyproject.toml`. Dist metadata is only as fresh as the last `pip install`; the editable install on the dev box reports `0.2.0`, so that test would have passed in CI and failed locally for a reason unrelated to the bug. Run locally with `PYTHONPATH=src` — the editable install resolves to the canonical checkout, not a worktree. ## Release Not bumping the release version — this repo lands those as separate `chore(release):` commits, and PyPI needs a `gh release create` to publish. This fix should land before the docs audit's Wave 1, since the docs mirror these model shapes and the SDK is the source they follow. Type: fix --------- Co-authored-by: root <root@api-dev.hs.chocopancake.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the structured
team_side+market_segmentaxes the SharpAPI now emits (sharp-api-go #76 Phase C / #689) to the response models.OddsLine,EVOpportunity,ClosingOddsLineeach gain optionalteam_side("home"|"away"|"draw") andmarket_segment("full_game","1st_half", ...).None; existing code unaffected.selection_typefor back-compat (Go re-synthesizes it), so this is purely additive on the consumer side.Smoke-tested model instantiation with the new fields. Bump 0.3.3 → 0.3.4.
Type: feat