xrtm-data 0.3.2__tar.gz → 0.5.0__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.
Files changed (32) hide show
  1. {xrtm_data-0.3.2/src/xrtm_data.egg-info → xrtm_data-0.5.0}/PKG-INFO +2 -2
  2. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/README.md +1 -1
  3. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/pyproject.toml +1 -1
  4. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/__init__.py +6 -0
  5. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/core/schemas/__init__.py +7 -0
  6. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/core/schemas/forecast.py +63 -0
  7. xrtm_data-0.5.0/src/xrtm/data/core/schemas/market.py +82 -0
  8. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/version.py +1 -1
  9. {xrtm_data-0.3.2 → xrtm_data-0.5.0/src/xrtm_data.egg-info}/PKG-INFO +2 -2
  10. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm_data.egg-info/SOURCES.txt +2 -0
  11. xrtm_data-0.5.0/tests/test_market_snapshot.py +63 -0
  12. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/tests/test_schemas.py +63 -1
  13. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/LICENSE +0 -0
  14. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/setup.cfg +0 -0
  15. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/core/__init__.py +0 -0
  16. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/core/interfaces.py +0 -0
  17. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/core/schemas/prior.py +0 -0
  18. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/core/schemas/trade.py +0 -0
  19. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/corpora/__init__.py +0 -0
  20. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/corpora/_builtin_corpora.py +0 -0
  21. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/corpora/real_binary.py +0 -0
  22. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/corpora/registry.py +0 -0
  23. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/providers/__init__.py +0 -0
  24. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/providers/online/__init__.py +0 -0
  25. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/providers/online/metaculus.py +0 -0
  26. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm/data/providers/online/polymarket.py +0 -0
  27. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm_data.egg-info/dependency_links.txt +0 -0
  28. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm_data.egg-info/requires.txt +0 -0
  29. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/src/xrtm_data.egg-info/top_level.txt +0 -0
  30. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/tests/test_corpus_registry.py +0 -0
  31. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/tests/test_prior_schemas.py +0 -0
  32. {xrtm_data-0.3.2 → xrtm_data-0.5.0}/tests/test_real_binary_corpus.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrtm-data
3
- Version: 0.3.2
3
+ Version: 0.5.0
4
4
  Summary: The Snapshot Vault for XRTM.
5
5
  Author-email: XRTM Team <moy@xrtm.org>
6
6
  License-Expression: Apache-2.0
@@ -17,7 +17,7 @@ Requires-Dist: ruff>=0.1.0; extra == "dev"
17
17
  Requires-Dist: mypy>=1.0.0; extra == "dev"
18
18
  Dynamic: license-file
19
19
 
20
- # xrtm-data v0.3.0
20
+ # xrtm-data v0.5.0
21
21
 
22
22
  [![PyPI](https://img.shields.io/pypi/v/xrtm-data?style=flat-square)](https://pypi.org/project/xrtm-data/)
23
23
 
@@ -1,4 +1,4 @@
1
- # xrtm-data v0.3.0
1
+ # xrtm-data v0.5.0
2
2
 
3
3
  [![PyPI](https://img.shields.io/pypi/v/xrtm-data?style=flat-square)](https://pypi.org/project/xrtm-data/)
4
4
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "xrtm-data"
7
- version = "0.3.2"
7
+ version = "0.5.0"
8
8
  description = "The Snapshot Vault for XRTM."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11,<3.13"
@@ -39,11 +39,14 @@ from xrtm.data.core.schemas import (
39
39
  CausalNode,
40
40
  ConfidenceInterval,
41
41
  ForecastOutput,
42
+ ForecastProvenance,
42
43
  ForecastQuestion,
43
44
  ForecastRequest,
44
45
  ForecastResult,
46
+ MarketSnapshot,
45
47
  MetadataBase,
46
48
  ReasoningTrace,
49
+ TokenUsage,
47
50
  )
48
51
 
49
52
  __all__ = [
@@ -63,4 +66,7 @@ __all__ = [
63
66
  "CausalGraph",
64
67
  "ReasoningTrace",
65
68
  "ConfidenceInterval",
69
+ "TokenUsage",
70
+ "ForecastProvenance",
71
+ "MarketSnapshot",
66
72
  ]
@@ -25,12 +25,15 @@ from xrtm.data.core.schemas.forecast import (
25
25
  CausalNode,
26
26
  ConfidenceInterval,
27
27
  ForecastOutput,
28
+ ForecastProvenance,
28
29
  ForecastQuestion,
29
30
  ForecastRequest,
30
31
  ForecastResult,
31
32
  MetadataBase,
32
33
  ReasoningTrace,
34
+ TokenUsage,
33
35
  )
36
+ from xrtm.data.core.schemas.market import MarketSnapshot
34
37
  from xrtm.data.core.schemas.prior import BetaPrior, PriorState
35
38
  from xrtm.data.core.schemas.trade import TradeEvent, TradeWindow
36
39
 
@@ -46,6 +49,10 @@ __all__ = [
46
49
  "CausalGraph",
47
50
  "ReasoningTrace",
48
51
  "ConfidenceInterval",
52
+ "TokenUsage",
53
+ "ForecastProvenance",
54
+ # Market schemas
55
+ "MarketSnapshot",
49
56
  # Prior schemas
50
57
  "BetaPrior",
51
58
  "PriorState",
@@ -178,6 +178,57 @@ class ConfidenceInterval(BaseModel):
178
178
  level: float = Field(0.9, ge=0, le=1, description="Confidence level")
179
179
 
180
180
 
181
+ class TokenUsage(BaseModel):
182
+ r"""
183
+ Token accounting for a single forecast or inference run.
184
+
185
+ Attributes:
186
+ prompt_tokens: Total input tokens billed for this run.
187
+ completion_tokens: Total output tokens billed for this run.
188
+ cached_prompt_tokens: Input tokens served from a provider prefix cache.
189
+ reasoning_tokens: Output tokens spent on chain-of-thought reasoning.
190
+ total_tokens: Total billed tokens (prompt + completion).
191
+ """
192
+
193
+ prompt_tokens: int = Field(default=0, ge=0, description="Total input tokens billed for this run")
194
+ completion_tokens: int = Field(default=0, ge=0, description="Total output tokens billed for this run")
195
+ cached_prompt_tokens: int = Field(default=0, ge=0, description="Input tokens served from a provider prefix cache")
196
+ reasoning_tokens: int = Field(default=0, ge=0, description="Output tokens spent on chain-of-thought reasoning")
197
+ total_tokens: int = Field(default=0, ge=0, description="Total billed tokens (prompt + completion)")
198
+
199
+ @model_validator(mode="after")
200
+ def _fill_total_tokens(self) -> "TokenUsage":
201
+ r"""Derive ``total_tokens`` when a producer did not provide it."""
202
+ if self.total_tokens == 0:
203
+ self.total_tokens = self.prompt_tokens + self.completion_tokens
204
+ return self
205
+
206
+
207
+ class ForecastProvenance(BaseModel):
208
+ r"""
209
+ Operational provenance for a forecast: which engine, model, and prompt produced it.
210
+
211
+ Attributes:
212
+ provider: Inference provider family (e.g. ``"deepseek"``, ``"typesafe"``).
213
+ model_id: Model identifier as requested (e.g. ``"deepseek-flash"``).
214
+ model_version: Model version or checkpoint reported by the provider.
215
+ prompt_id: Identifier or version of the prompt template used.
216
+ temperature: Sampling temperature used for the run.
217
+ thinking: Whether provider reasoning/thinking mode was enabled.
218
+ cache_hit: True when the result was served from a cache.
219
+ run_id: Identifier shared by all calls belonging to one forecast run.
220
+ """
221
+
222
+ provider: Optional[str] = Field(default=None, description="Inference provider family (e.g. 'deepseek')")
223
+ model_id: Optional[str] = Field(default=None, description="Model identifier as requested")
224
+ model_version: Optional[str] = Field(default=None, description="Model version or checkpoint reported by the provider")
225
+ prompt_id: Optional[str] = Field(default=None, description="Identifier or version of the prompt template used")
226
+ temperature: Optional[float] = Field(default=None, description="Sampling temperature used for the run")
227
+ thinking: Optional[bool] = Field(default=None, description="Whether provider reasoning/thinking mode was enabled")
228
+ cache_hit: bool = Field(default=False, description="True when the result was served from a cache")
229
+ run_id: Optional[str] = Field(default=None, description="Identifier shared by all calls belonging to one forecast run")
230
+
231
+
181
232
  class MappingCompatibleModel(BaseModel):
182
233
  r"""Base model with lightweight dict-style compatibility helpers."""
183
234
 
@@ -293,6 +344,16 @@ class ForecastOutput(BaseModel):
293
344
  description="Ordered workflow stages executed for this forecast result",
294
345
  )
295
346
  calibration_metrics: Dict[str, Any] = Field(default_factory=dict, description="Performance metrics")
347
+ parse_status: str = Field(
348
+ default="unknown",
349
+ description="Parse outcome for the model output: 'ok', 'empty_content', 'invalid_json', "
350
+ "'schema_error', 'provider_error', or 'unknown'.",
351
+ )
352
+ usage: TokenUsage = Field(default_factory=TokenUsage, description="Token accounting for this forecast")
353
+ provenance: Optional[ForecastProvenance] = Field(
354
+ default=None,
355
+ description="Model and prompt provenance for this forecast",
356
+ )
296
357
  metadata: MetadataBase = Field(default_factory=MetadataBase) # type: ignore[arg-type]
297
358
 
298
359
  @model_validator(mode="before")
@@ -458,6 +519,8 @@ __all__ = [
458
519
  "ReasoningTrace",
459
520
  "ForecastOutput",
460
521
  "ForecastResult",
522
+ "TokenUsage",
523
+ "ForecastProvenance",
461
524
  ]
462
525
 
463
526
  ForecastResult = ForecastOutput
@@ -0,0 +1,82 @@
1
+ # coding=utf-8
2
+ # Copyright 2026 XRTM Team. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ r"""Point-in-time market snapshot schema.
17
+
18
+ A shared contract for prediction-market state (price, book, liquidity) that
19
+ applications, evaluators, and backtests can all consume without re-inventing
20
+ venue-specific shapes.
21
+
22
+ Zero Leakage: every snapshot carries the ``snapshot_time`` at which the world
23
+ state was frozen; no data after that instant should influence a forecast.
24
+ """
25
+
26
+ from datetime import datetime, timezone
27
+ from typing import Any, Dict, Optional
28
+
29
+ from pydantic import BaseModel, Field, field_validator, model_validator
30
+
31
+ __all__ = ["MarketSnapshot"]
32
+
33
+
34
+ class MarketSnapshot(BaseModel):
35
+ r"""Point-in-time state of a prediction market.
36
+
37
+ Attributes:
38
+ market_id: Venue-specific market identifier.
39
+ question_id: Optional link to the ``ForecastQuestion`` this market resolves.
40
+ probability: Implied/mid probability of the primary outcome (0-1).
41
+ best_bid: Best bid price (0-1).
42
+ best_ask: Best ask price (0-1).
43
+ spread: Bid/ask spread (0-1), derived from bid/ask when omitted.
44
+ volume_24h: Trailing 24h volume, in the venue's currency.
45
+ liquidity: Available liquidity, in the venue's currency.
46
+ snapshot_time: The "Time T" at which the market state was frozen.
47
+ source: Venue/source identifier (e.g. ``"polymarket"``).
48
+ raw_data: Original venue payload for debugging/audit.
49
+ """
50
+
51
+ market_id: str = Field(..., description="Venue-specific market identifier")
52
+ question_id: Optional[str] = Field(default=None, description="Linked ForecastQuestion id")
53
+ probability: Optional[float] = Field(default=None, ge=0, le=1, description="Implied/mid probability")
54
+ best_bid: Optional[float] = Field(default=None, ge=0, le=1, description="Best bid price")
55
+ best_ask: Optional[float] = Field(default=None, ge=0, le=1, description="Best ask price")
56
+ spread: Optional[float] = Field(default=None, ge=0, le=1, description="Bid/ask spread")
57
+ volume_24h: Optional[float] = Field(default=None, ge=0, description="Trailing 24h volume")
58
+ liquidity: Optional[float] = Field(default=None, ge=0, description="Available liquidity")
59
+ snapshot_time: datetime = Field(
60
+ default_factory=lambda: datetime.now(timezone.utc),
61
+ description="Zero Leakage: the instant this market state was frozen",
62
+ )
63
+ source: Optional[str] = Field(default=None, description="Venue/source identifier")
64
+ raw_data: Optional[Dict[str, Any]] = Field(default=None, description="Original venue payload")
65
+
66
+ @field_validator("snapshot_time", mode="after")
67
+ @classmethod
68
+ def _normalize_snapshot_time(cls, value: datetime) -> datetime:
69
+ r"""Store the snapshot boundary as a timezone-aware UTC datetime."""
70
+ if value.tzinfo is None:
71
+ return value.replace(tzinfo=timezone.utc)
72
+ return value.astimezone(timezone.utc)
73
+
74
+ @model_validator(mode="after")
75
+ def _validate_book(self) -> "MarketSnapshot":
76
+ r"""Derive the spread and reject crossed books."""
77
+ if self.best_bid is not None and self.best_ask is not None:
78
+ if self.best_bid > self.best_ask:
79
+ raise ValueError("best_bid must not exceed best_ask")
80
+ if self.spread is None:
81
+ self.spread = round(self.best_ask - self.best_bid, 10)
82
+ return self
@@ -21,7 +21,7 @@ This module provides the single source of truth for the package version.
21
21
 
22
22
  __all__ = ["__version__", "__author__", "__contact__", "__license__", "__copyright__"]
23
23
 
24
- __version__ = "0.3.2"
24
+ __version__ = "0.5.0"
25
25
  __author__ = "XRTM Team"
26
26
  __contact__ = "moy@xrtm.org"
27
27
  __license__ = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrtm-data
3
- Version: 0.3.2
3
+ Version: 0.5.0
4
4
  Summary: The Snapshot Vault for XRTM.
5
5
  Author-email: XRTM Team <moy@xrtm.org>
6
6
  License-Expression: Apache-2.0
@@ -17,7 +17,7 @@ Requires-Dist: ruff>=0.1.0; extra == "dev"
17
17
  Requires-Dist: mypy>=1.0.0; extra == "dev"
18
18
  Dynamic: license-file
19
19
 
20
- # xrtm-data v0.3.0
20
+ # xrtm-data v0.5.0
21
21
 
22
22
  [![PyPI](https://img.shields.io/pypi/v/xrtm-data?style=flat-square)](https://pypi.org/project/xrtm-data/)
23
23
 
@@ -7,6 +7,7 @@ src/xrtm/data/core/__init__.py
7
7
  src/xrtm/data/core/interfaces.py
8
8
  src/xrtm/data/core/schemas/__init__.py
9
9
  src/xrtm/data/core/schemas/forecast.py
10
+ src/xrtm/data/core/schemas/market.py
10
11
  src/xrtm/data/core/schemas/prior.py
11
12
  src/xrtm/data/core/schemas/trade.py
12
13
  src/xrtm/data/corpora/__init__.py
@@ -23,6 +24,7 @@ src/xrtm_data.egg-info/dependency_links.txt
23
24
  src/xrtm_data.egg-info/requires.txt
24
25
  src/xrtm_data.egg-info/top_level.txt
25
26
  tests/test_corpus_registry.py
27
+ tests/test_market_snapshot.py
26
28
  tests/test_prior_schemas.py
27
29
  tests/test_real_binary_corpus.py
28
30
  tests/test_schemas.py
@@ -0,0 +1,63 @@
1
+ # coding=utf-8
2
+ # Copyright 2026 XRTM Team. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ r"""Tests for the MarketSnapshot schema."""
17
+
18
+ from datetime import datetime, timezone
19
+
20
+ import pytest
21
+
22
+ from xrtm.data import MarketSnapshot
23
+
24
+
25
+ def test_snapshot_normalizes_naive_time_to_utc():
26
+ snapshot = MarketSnapshot(market_id="m1", snapshot_time=datetime(2026, 9, 26, 12, 0))
27
+ assert snapshot.snapshot_time.tzinfo == timezone.utc
28
+
29
+
30
+ def test_spread_is_derived_from_book():
31
+ snapshot = MarketSnapshot(market_id="m2", best_bid=0.40, best_ask=0.44)
32
+ assert snapshot.spread == pytest.approx(0.04)
33
+
34
+
35
+ def test_explicit_spread_is_preserved():
36
+ snapshot = MarketSnapshot(market_id="m3", best_bid=0.40, best_ask=0.44, spread=0.05)
37
+ assert snapshot.spread == pytest.approx(0.05)
38
+
39
+
40
+ def test_crossed_book_is_rejected():
41
+ with pytest.raises(ValueError, match="best_bid"):
42
+ MarketSnapshot(market_id="m4", best_bid=0.60, best_ask=0.50)
43
+
44
+
45
+ def test_snapshot_serializes_and_round_trips():
46
+ snapshot = MarketSnapshot(
47
+ market_id="m5",
48
+ question_id="q5",
49
+ probability=0.42,
50
+ best_bid=0.41,
51
+ best_ask=0.43,
52
+ volume_24h=150_000,
53
+ liquidity=20_000,
54
+ source="polymarket",
55
+ raw_data={"token_id": "123"},
56
+ )
57
+ payload = snapshot.model_dump(mode="json")
58
+ assert payload["spread"] == pytest.approx(0.02)
59
+
60
+ reloaded = MarketSnapshot.model_validate(payload)
61
+ assert reloaded.market_id == "m5"
62
+ assert reloaded.question_id == "q5"
63
+ assert reloaded.raw_data == {"token_id": "123"}
@@ -16,7 +16,16 @@ from datetime import datetime, timedelta, timezone
16
16
 
17
17
  import pytest
18
18
 
19
- from xrtm.data import CausalEdge, CausalNode, ForecastOutput, ForecastQuestion, ForecastResult, MetadataBase
19
+ from xrtm.data import (
20
+ CausalEdge,
21
+ CausalNode,
22
+ ForecastOutput,
23
+ ForecastProvenance,
24
+ ForecastQuestion,
25
+ ForecastResult,
26
+ MetadataBase,
27
+ TokenUsage,
28
+ )
20
29
  from xrtm.data.core.schemas import TradeEvent, TradeWindow
21
30
 
22
31
 
@@ -202,3 +211,56 @@ def test_forecast_question_context_serializes():
202
211
 
203
212
  reloaded = ForecastQuestion.model_validate(payload)
204
213
  assert reloaded.context == {"key": "value", "nested": {"a": 1}}
214
+
215
+
216
+ def test_token_usage_derives_total():
217
+ """total_tokens is derived when a producer omits it."""
218
+ usage = TokenUsage(prompt_tokens=1200, completion_tokens=300)
219
+ assert usage.total_tokens == 1500
220
+
221
+
222
+ def test_token_usage_preserves_explicit_total():
223
+ """An explicit total_tokens from the provider is not overwritten."""
224
+ usage = TokenUsage(prompt_tokens=10, completion_tokens=5, total_tokens=99)
225
+ assert usage.total_tokens == 99
226
+
227
+
228
+ def test_forecast_output_telemetry_defaults():
229
+ """Telemetry fields are optional and safe for legacy producers."""
230
+ output = ForecastOutput(question_id="q_telemetry", probability=0.5, reasoning="legacy")
231
+ assert output.parse_status == "unknown"
232
+ assert output.usage.total_tokens == 0
233
+ assert output.provenance is None
234
+
235
+
236
+ def test_forecast_output_telemetry_round_trip():
237
+ """Telemetry survives JSON serialization and re-validation."""
238
+ output = ForecastOutput(
239
+ forecast_request_id="q_telemetry_2",
240
+ probability=0.72,
241
+ reasoning="telemetry round trip",
242
+ parse_status="ok",
243
+ usage=TokenUsage(
244
+ prompt_tokens=900,
245
+ completion_tokens=250,
246
+ cached_prompt_tokens=800,
247
+ reasoning_tokens=120,
248
+ ),
249
+ provenance=ForecastProvenance(
250
+ provider="deepseek",
251
+ model_id="deepseek-flash",
252
+ prompt_id="analyst-v2",
253
+ temperature=0.2,
254
+ thinking=False,
255
+ cache_hit=False,
256
+ ),
257
+ )
258
+ payload = output.model_dump(mode="json")
259
+ assert payload["parse_status"] == "ok"
260
+ assert payload["usage"]["total_tokens"] == 1150
261
+ assert payload["provenance"]["model_id"] == "deepseek-flash"
262
+
263
+ reloaded = ForecastOutput.model_validate(payload)
264
+ assert reloaded.usage.cached_prompt_tokens == 800
265
+ assert reloaded.provenance is not None
266
+ assert reloaded.provenance.provider == "deepseek"
File without changes
File without changes