pmlab 0.1.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 (101) hide show
  1. pmlab-0.1.0/.github/workflows/ci.yml +58 -0
  2. pmlab-0.1.0/.gitignore +20 -0
  3. pmlab-0.1.0/AGENTS.md +155 -0
  4. pmlab-0.1.0/CHANGELOG.md +31 -0
  5. pmlab-0.1.0/LICENSE +21 -0
  6. pmlab-0.1.0/PKG-INFO +490 -0
  7. pmlab-0.1.0/README.md +447 -0
  8. pmlab-0.1.0/docs/plugin-authoring.md +135 -0
  9. pmlab-0.1.0/pyproject.toml +91 -0
  10. pmlab-0.1.0/scripts/pmlab-workspace +17 -0
  11. pmlab-0.1.0/src/pmlab/__init__.py +63 -0
  12. pmlab-0.1.0/src/pmlab/backtest/__init__.py +0 -0
  13. pmlab-0.1.0/src/pmlab/backtest/holdout_gate.py +127 -0
  14. pmlab-0.1.0/src/pmlab/backtest/metrics.py +53 -0
  15. pmlab-0.1.0/src/pmlab/backtest/rolling_origin.py +142 -0
  16. pmlab-0.1.0/src/pmlab/cli/__init__.py +0 -0
  17. pmlab-0.1.0/src/pmlab/cli/main.py +83 -0
  18. pmlab-0.1.0/src/pmlab/core/__init__.py +0 -0
  19. pmlab-0.1.0/src/pmlab/core/edge.py +27 -0
  20. pmlab-0.1.0/src/pmlab/core/fees.py +21 -0
  21. pmlab-0.1.0/src/pmlab/core/market_spec.py +73 -0
  22. pmlab-0.1.0/src/pmlab/core/pnl.py +48 -0
  23. pmlab-0.1.0/src/pmlab/core/sizing.py +72 -0
  24. pmlab-0.1.0/src/pmlab/data/__init__.py +0 -0
  25. pmlab-0.1.0/src/pmlab/execution/__init__.py +0 -0
  26. pmlab-0.1.0/src/pmlab/execution/edge_signal.py +23 -0
  27. pmlab-0.1.0/src/pmlab/execution/live_broker.py +187 -0
  28. pmlab-0.1.0/src/pmlab/execution/paper_broker.py +141 -0
  29. pmlab-0.1.0/src/pmlab/execution/settlement.py +131 -0
  30. pmlab-0.1.0/src/pmlab/features/__init__.py +12 -0
  31. pmlab-0.1.0/src/pmlab/features/transforms.py +95 -0
  32. pmlab-0.1.0/src/pmlab/markets/__init__.py +0 -0
  33. pmlab-0.1.0/src/pmlab/markets/async_clob_client.py +43 -0
  34. pmlab-0.1.0/src/pmlab/markets/async_gamma_client.py +33 -0
  35. pmlab-0.1.0/src/pmlab/markets/cache.py +70 -0
  36. pmlab-0.1.0/src/pmlab/markets/clob_client.py +71 -0
  37. pmlab-0.1.0/src/pmlab/markets/gamma_client.py +106 -0
  38. pmlab-0.1.0/src/pmlab/modeling/__init__.py +0 -0
  39. pmlab-0.1.0/src/pmlab/modeling/base.py +38 -0
  40. pmlab-0.1.0/src/pmlab/modeling/calibration.py +45 -0
  41. pmlab-0.1.0/src/pmlab/modeling/champion.py +113 -0
  42. pmlab-0.1.0/src/pmlab/modeling/diagnostics.py +67 -0
  43. pmlab-0.1.0/src/pmlab/modeling/lgbm_baseline.py +69 -0
  44. pmlab-0.1.0/src/pmlab/plugins/__init__.py +0 -0
  45. pmlab-0.1.0/src/pmlab/plugins/base.py +105 -0
  46. pmlab-0.1.0/src/pmlab/plugins/registry.py +38 -0
  47. pmlab-0.1.0/src/pmlab/plugins/sports_f1/__init__.py +0 -0
  48. pmlab-0.1.0/src/pmlab/plugins/sports_f1/plugin.py +103 -0
  49. pmlab-0.1.0/src/pmlab/plugins/weather_tmax/__init__.py +0 -0
  50. pmlab-0.1.0/src/pmlab/plugins/weather_tmax/_spec_builder.py +78 -0
  51. pmlab-0.1.0/src/pmlab/plugins/weather_tmax/plugin.py +112 -0
  52. pmlab-0.1.0/src/pmlab/py.typed +0 -0
  53. pmlab-0.1.0/src/pmlab/reports/__init__.py +4 -0
  54. pmlab-0.1.0/src/pmlab/reports/html_report.py +165 -0
  55. pmlab-0.1.0/src/pmlab/workspace/__init__.py +0 -0
  56. pmlab-0.1.0/src/pmlab/workspace/context.py +36 -0
  57. pmlab-0.1.0/tests/__init__.py +0 -0
  58. pmlab-0.1.0/tests/backtest/__init__.py +0 -0
  59. pmlab-0.1.0/tests/backtest/test_holdout_gate.py +106 -0
  60. pmlab-0.1.0/tests/backtest/test_metrics.py +54 -0
  61. pmlab-0.1.0/tests/backtest/test_rolling_origin.py +133 -0
  62. pmlab-0.1.0/tests/conftest.py +14 -0
  63. pmlab-0.1.0/tests/core/__init__.py +0 -0
  64. pmlab-0.1.0/tests/core/test_edge.py +26 -0
  65. pmlab-0.1.0/tests/core/test_fees.py +24 -0
  66. pmlab-0.1.0/tests/core/test_kelly_sizing.py +57 -0
  67. pmlab-0.1.0/tests/core/test_market_spec.py +135 -0
  68. pmlab-0.1.0/tests/core/test_pnl.py +48 -0
  69. pmlab-0.1.0/tests/core/test_sizing.py +26 -0
  70. pmlab-0.1.0/tests/execution/__init__.py +0 -0
  71. pmlab-0.1.0/tests/execution/test_edge_signal.py +49 -0
  72. pmlab-0.1.0/tests/execution/test_live_broker.py +125 -0
  73. pmlab-0.1.0/tests/execution/test_paper_broker.py +105 -0
  74. pmlab-0.1.0/tests/execution/test_settlement.py +308 -0
  75. pmlab-0.1.0/tests/features/__init__.py +0 -0
  76. pmlab-0.1.0/tests/features/test_transforms.py +91 -0
  77. pmlab-0.1.0/tests/integration/__init__.py +0 -0
  78. pmlab-0.1.0/tests/integration/test_full_pipeline.py +146 -0
  79. pmlab-0.1.0/tests/markets/__init__.py +0 -0
  80. pmlab-0.1.0/tests/markets/test_async_clients.py +53 -0
  81. pmlab-0.1.0/tests/markets/test_cache.py +56 -0
  82. pmlab-0.1.0/tests/markets/test_clob_client.py +43 -0
  83. pmlab-0.1.0/tests/markets/test_gamma_client.py +59 -0
  84. pmlab-0.1.0/tests/modeling/__init__.py +0 -0
  85. pmlab-0.1.0/tests/modeling/test_base.py +39 -0
  86. pmlab-0.1.0/tests/modeling/test_calibration.py +33 -0
  87. pmlab-0.1.0/tests/modeling/test_champion.py +162 -0
  88. pmlab-0.1.0/tests/modeling/test_diagnostics.py +52 -0
  89. pmlab-0.1.0/tests/modeling/test_lgbm.py +56 -0
  90. pmlab-0.1.0/tests/plugins/__init__.py +0 -0
  91. pmlab-0.1.0/tests/plugins/sports_f1/__init__.py +0 -0
  92. pmlab-0.1.0/tests/plugins/sports_f1/test_sports_f1_plugin.py +109 -0
  93. pmlab-0.1.0/tests/plugins/test_plugin_base.py +97 -0
  94. pmlab-0.1.0/tests/plugins/test_registry.py +60 -0
  95. pmlab-0.1.0/tests/plugins/weather_tmax/__init__.py +0 -0
  96. pmlab-0.1.0/tests/plugins/weather_tmax/test_weather_tmax_plugin.py +174 -0
  97. pmlab-0.1.0/tests/reports/__init__.py +0 -0
  98. pmlab-0.1.0/tests/reports/test_html_report.py +34 -0
  99. pmlab-0.1.0/tests/test_smoke.py +98 -0
  100. pmlab-0.1.0/tests/test_workspace.py +60 -0
  101. pmlab-0.1.0/uv.lock +911 -0
@@ -0,0 +1,58 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, "feat/**", "fix/**"]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: Test (Python 3.12)
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+
22
+ - name: Install uv
23
+ uses: astral-sh/setup-uv@v5
24
+ with:
25
+ version: "latest"
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --extra dev
29
+
30
+ - name: Lint (ruff)
31
+ run: uv run ruff check src/ tests/
32
+
33
+ - name: Run tests with coverage
34
+ run: uv run pytest --cov=src/pmlab --cov-report=term-missing --cov-fail-under=70
35
+
36
+ build:
37
+ name: Build distribution
38
+ runs-on: ubuntu-latest
39
+ needs: test
40
+
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+
44
+ - name: Set up Python
45
+ uses: actions/setup-python@v5
46
+ with:
47
+ python-version: "3.12"
48
+
49
+ - name: Install uv
50
+ uses: astral-sh/setup-uv@v5
51
+ with:
52
+ version: "latest"
53
+
54
+ - name: Build wheel + sdist
55
+ run: uv build
56
+
57
+ - name: Verify dist contents
58
+ run: ls -la dist/
pmlab-0.1.0/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ # gitignore para Python library
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ .venv/
11
+ .env
12
+ *.lock
13
+ !uv.lock
14
+ .pytest_cache/
15
+ .mypy_cache/
16
+ .ruff_cache/
17
+ .coverage
18
+ htmlcov/
19
+ *.log
20
+ .DS_Store
pmlab-0.1.0/AGENTS.md ADDED
@@ -0,0 +1,155 @@
1
+ # pmlab Agent Guide
2
+
3
+ ## Purpose
4
+
5
+ `pmlab` is a **generic, plugin-based ML framework** for Polymarket prediction markets.
6
+ It is designed to be published as a Python library (pip/uv installable).
7
+
8
+ The framework is market-family agnostic: each domain (weather, sports, politics, crypto)
9
+ implements a `MarketPlugin` and the core handles everything else.
10
+
11
+ Use this file as the shared contract for all agents (Codex, Claude, etc).
12
+
13
+ ---
14
+
15
+ ## Module Ownership
16
+
17
+ | Module | Owns |
18
+ |--------|------|
19
+ | `src/pmlab/core/` | MarketSpec, OutcomeBin, PnL, edge, fees, sizing — no domain logic |
20
+ | `src/pmlab/plugins/` | MarketPlugin ABC, PluginRegistry, domain plugin implementations |
21
+ | `src/pmlab/plugins/weather_tmax/` | Weather/temperature market plugin (reference implementation) |
22
+ | `src/pmlab/plugins/sports_f1/` | F1 race outcome plugin |
23
+ | `src/pmlab/backtest/` | Rolling-origin eval, metrics, HoldoutGate — no lookahead |
24
+ | `src/pmlab/modeling/` | MarketForecaster ABC, LGBMForecaster, calibration, ChampionManifest |
25
+ | `src/pmlab/execution/` | EdgeSignal, PaperBroker, SettlementEngine |
26
+ | `src/pmlab/workspace/` | WorkspaceContext — multi-workspace path isolation |
27
+ | `src/pmlab/markets/` | Polymarket Gamma API client, CLOB price client (Phase 4) |
28
+ | `src/pmlab/cli/` | Typer CLI entry points |
29
+
30
+ ---
31
+
32
+ ## Safety Rules
33
+
34
+ 1. **Never publish a champion with NO_GO gate.**
35
+ `ChampionManifest.publish()` hard-raises `ValueError` if `gate.decision != "GO"`.
36
+ This is not configurable. A failed gate must be investigated, not bypassed.
37
+
38
+ 2. **Never overwrite canonical training data without explicit `--allow-overwrite`.**
39
+ Experiments use variant output paths. Canonical data is for stable baselines only.
40
+
41
+ 3. **Never mix workspace data.**
42
+ `ops_daily` is live forward evidence only. `historical_real` is training data.
43
+ A model trained on ops_daily data is contaminated — do not promote it.
44
+
45
+ 4. **Always use `scripts/pmlab-workspace <name> <cmd>` for workspace-scoped operations.**
46
+ This sets `PMLAB_ARTIFACTS_DIR` and friends so all paths resolve correctly.
47
+
48
+ 5. **City/segment gate always reads from `champion.json`.**
49
+ The `recent_core_benchmark_summary.json` (or equivalent) is overwritten on each retrain.
50
+ `champion.json` reflects the gate at promotion time and is the authoritative source.
51
+
52
+ 6. **Never settle trades before `plugin.is_truth_final()` returns True.**
53
+ Some data sources have a lag (e.g. weather stations). Wait for finalization.
54
+
55
+ ---
56
+
57
+ ## Adding a New Plugin
58
+
59
+ 1. Create `src/pmlab/plugins/<your_family>/plugin.py`
60
+ 2. Subclass `MarketPlugin` from `pmlab.plugins.base`
61
+ 3. Set `family = "your_family"`
62
+ 4. Implement all 4 abstract methods: `discover_markets`, `fetch_features`, `fetch_truth`, `build_training_row`
63
+ 5. Optionally override `is_truth_final()` if your data source has a finalization lag
64
+ 6. Add tests in `tests/plugins/<your_family>/`
65
+ 7. Register via `PluginRegistry.register(YourPlugin())`
66
+
67
+ See `src/pmlab/plugins/weather_tmax/plugin.py` as the reference implementation.
68
+ See `docs/plugin-authoring.md` for the full guide.
69
+
70
+ ---
71
+
72
+ ## Standard Commands
73
+
74
+ ```bash
75
+ # Install in development mode
76
+ uv sync --extra dev
77
+
78
+ # Run all tests
79
+ uv run pytest
80
+
81
+ # Run tests with coverage
82
+ uv run pytest --cov=src/pmlab --cov-report=term-missing
83
+
84
+ # Lint
85
+ uv run ruff check src/ tests/
86
+
87
+ # Type check
88
+ uv run mypy src/
89
+
90
+ # Workspace-scoped command
91
+ scripts/pmlab-workspace ops_daily uv run pmlab scan-edge --plugin weather_tmax
92
+
93
+ # Build distribution
94
+ uv build
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Commit Convention
100
+
101
+ Format: `<type>: <subject>` — imperative, no period.
102
+
103
+ Types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `ci`, `perf`
104
+
105
+ Examples:
106
+ - `feat: add SportsF1Plugin with categorical outcome bins`
107
+ - `fix: champion gate reads from champion.json not retrain summary`
108
+ - `test: add rolling_origin no-lookahead assertion`
109
+ - `docs: update plugin-authoring guide with is_truth_final`
110
+
111
+ ---
112
+
113
+ ## TDD Contract
114
+
115
+ Every new module gets tests BEFORE implementation:
116
+ 1. Write test → run → confirm RED (import error or assertion failure)
117
+ 2. Implement minimal code → run → confirm GREEN
118
+ 3. Refactor → run → confirm still GREEN
119
+ 4. Commit
120
+
121
+ Coverage gate: 70% minimum (enforced in pyproject.toml).
122
+
123
+ ---
124
+
125
+ ## Architecture Summary
126
+
127
+ ```
128
+ Polymarket API ──► MarketPlugin.discover_markets() ──► list[MarketSpec]
129
+
130
+ ├──► MarketPlugin.fetch_features() ──► features dict
131
+ ├──► MarketPlugin.fetch_truth() ──► realized outcome
132
+ └──► MarketPlugin.build_training_row() ──► training row
133
+
134
+ ┌─────────────────────────┘
135
+
136
+
137
+ rolling_origin_eval(panel, model)
138
+
139
+ ├──► BacktestMetrics (PnL, hit_rate, edge)
140
+ └──► HoldoutGate.evaluate(required_segments) ──► GO / NO_GO
141
+
142
+ GO
143
+
144
+ ChampionManifest.publish(model, gate)
145
+ (hard-raises on NO_GO)
146
+
147
+ champion.json
148
+ champion.pkl
149
+
150
+ PaperBroker.record(signals)
151
+ (reads allowed_segments from champion.json)
152
+
153
+ SettlementEngine.settle_all(specs)
154
+ (calls plugin.fetch_truth + is_truth_final)
155
+ ```
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ All notable changes are documented here. Format: [Keep a Changelog](https://keepachangelog.com/).
4
+
5
+ ## [0.1.0] — 2026-05-10
6
+
7
+ ### Added
8
+ - `MarketPlugin` ABC — interface for domain-specific plugins
9
+ - `PluginRegistry` — register and look up plugins by family name
10
+ - `WeatherTmaxPlugin` — reference implementation for temperature markets
11
+ - `SportsF1Plugin` — categorical outcome plugin for F1 race markets
12
+ - `MarketSpec` + `OutcomeBin` — generic market descriptor
13
+ - `Position` + `settle_position` — binary outcome PnL accounting
14
+ - `compute_edge` — after-cost probability edge
15
+ - `estimate_fee` + `flat_stake_size` — trade sizing utilities
16
+ - `rolling_origin_eval` — walk-forward backtest with no-lookahead guarantee
17
+ - `BacktestMetrics` + `compute_metrics` — PnL, hit rate, edge statistics
18
+ - `HoldoutGateResult` — per-segment GO/NO_GO gate
19
+ - `MarketForecaster` ABC — model protocol
20
+ - `LGBMForecaster` — LightGBM binary/multiclass implementation
21
+ - `IsotonicCalibrator` — probability calibration
22
+ - `ChampionManifest` — hard-gate champion publish (raises on NO_GO)
23
+ - `EdgeSignal` — typed scan-edge output dataclass
24
+ - `PaperBroker` — record trades with segment gate, stale guard, dedup
25
+ - `SettlementEngine` — settle open trades via plugin truth resolution
26
+ - `WorkspaceContext` + `scripts/pmlab-workspace` — workspace isolation
27
+ - `GammaClient` + `fetch_gamma_markets` — Polymarket Gamma API
28
+ - `ClobClient` + `fetch_token_prices` — Polymarket CLOB price fetch
29
+ - CLI: `pmlab version`, `status`, `scan-markets`, `record-trades`, `settle-trades`, `backtest`, `promote-champion`
30
+ - GitHub Actions CI: lint (ruff) + pytest (70% coverage gate) + build
31
+ - 123+ tests, 92%+ coverage
pmlab-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arthur Breguez
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.