polymarket-backtest 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 (22) hide show
  1. polymarket_backtest-0.1.0/PKG-INFO +216 -0
  2. polymarket_backtest-0.1.0/README.md +188 -0
  3. polymarket_backtest-0.1.0/polymarket_backtest/__init__.py +35 -0
  4. polymarket_backtest-0.1.0/polymarket_backtest/api/__init__.py +13 -0
  5. polymarket_backtest-0.1.0/polymarket_backtest/api/clob.py +136 -0
  6. polymarket_backtest-0.1.0/polymarket_backtest/api/gamma.py +194 -0
  7. polymarket_backtest-0.1.0/polymarket_backtest/api/models.py +69 -0
  8. polymarket_backtest-0.1.0/polymarket_backtest/backtest/__init__.py +21 -0
  9. polymarket_backtest-0.1.0/polymarket_backtest/backtest/metrics.py +286 -0
  10. polymarket_backtest-0.1.0/polymarket_backtest/data/__init__.py +11 -0
  11. polymarket_backtest-0.1.0/polymarket_backtest/data/loader.py +136 -0
  12. polymarket_backtest-0.1.0/polymarket_backtest/data/sample/btc_orderbook.csv +13750 -0
  13. polymarket_backtest-0.1.0/polymarket_backtest/data/sample/flash_crash_summary.csv +3 -0
  14. polymarket_backtest-0.1.0/polymarket_backtest/data/sample/flash_crash_trades.csv +23 -0
  15. polymarket_backtest-0.1.0/polymarket_backtest/data/sample/hedge_arb_trades.csv +5 -0
  16. polymarket_backtest-0.1.0/polymarket_backtest.egg-info/PKG-INFO +216 -0
  17. polymarket_backtest-0.1.0/polymarket_backtest.egg-info/SOURCES.txt +20 -0
  18. polymarket_backtest-0.1.0/polymarket_backtest.egg-info/dependency_links.txt +1 -0
  19. polymarket_backtest-0.1.0/polymarket_backtest.egg-info/requires.txt +7 -0
  20. polymarket_backtest-0.1.0/polymarket_backtest.egg-info/top_level.txt +1 -0
  21. polymarket_backtest-0.1.0/pyproject.toml +46 -0
  22. polymarket_backtest-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,216 @@
1
+ Metadata-Version: 2.4
2
+ Name: polymarket-backtest
3
+ Version: 0.1.0
4
+ Summary: Polymarket API wrapper and strategy backtesting metrics toolkit
5
+ Author-email: Your Name <you@example.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/yourname/polymarket-backtest
8
+ Project-URL: Repository, https://github.com/yourname/polymarket-backtest
9
+ Project-URL: Issues, https://github.com/yourname/polymarket-backtest/issues
10
+ Keywords: polymarket,prediction-market,backtest,trading,quantitative
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Financial and Insurance Industry
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Office/Business :: Financial
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: requests>=2.28
23
+ Requires-Dist: numpy>=1.24
24
+ Requires-Dist: pandas>=2.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
28
+
29
+ # polymarket-backtest
30
+
31
+ Polymarket API 封装与策略回测评估工具包。
32
+
33
+ ## 功能
34
+
35
+ - **API 封装**:Polymarket Gamma API(合约信息)、CLOB API(赔率历史时间序列)
36
+ - **内置数据集**:BTC 15 分钟市场盘口快照 + Flash Crash / Hedge Arb 策略回测记录
37
+ - **回测指标**:Sharpe Ratio、最大回撤、胜率、盈亏比、卡玛比率
38
+
39
+ ## 安装
40
+
41
+ ```bash
42
+ pip install -e /path/to/polymarket_backtest
43
+ # 或
44
+ cd /path/to/polymarket_backtest && pip install -e .
45
+ ```
46
+
47
+ ## 快速开始
48
+
49
+ ### 1. 查询合约信息
50
+
51
+ ```python
52
+ from polymarket_backtest.api import GammaClient
53
+
54
+ gamma = GammaClient()
55
+
56
+ # 获取当前活跃的 BTC 15 分钟市场
57
+ market = gamma.get_market_info("BTC")
58
+ print(market.slug) # "btc-updown-15m-1775035200"
59
+ print(market.up_price) # 0.52
60
+ print(market.down_price) # 0.48
61
+ print(market.up_token_id) # "229931..."
62
+
63
+ # 列出最近 5 个市场
64
+ markets = gamma.list_recent_markets("ETH", n=5)
65
+ for m in markets:
66
+ print(m.slug, m.end_date)
67
+ ```
68
+
69
+ ### 2. 拉取赔率历史
70
+
71
+ ```python
72
+ from polymarket_backtest.api import GammaClient, ClobClient
73
+
74
+ gamma = GammaClient()
75
+ clob = ClobClient()
76
+
77
+ market = gamma.get_market_info("BTC")
78
+
79
+ # 拉取最近 1 天数据(每小时 1 个点)
80
+ history = clob.get_price_history(
81
+ market.up_token_id,
82
+ interval="1d",
83
+ fidelity=60,
84
+ )
85
+ print(f"获取到 {len(history)} 个价格点")
86
+
87
+ for point in history.points[:3]:
88
+ print(point.timestamp, point.price)
89
+
90
+ # 直接返回 DataFrame
91
+ df = clob.get_price_history_df(market.up_token_id, interval="1w", fidelity=60)
92
+ print(df.head())
93
+ # timestamp price datetime
94
+ # 0 1697875200 0.520 2023-10-21 08:00:00+00:00
95
+ ```
96
+
97
+ ### 3. 加载内置数据集
98
+
99
+ ```python
100
+ from polymarket_backtest.data import list_datasets, load_orderbook, load_trades, load_summary
101
+
102
+ # 查看可用数据集
103
+ for ds in list_datasets():
104
+ print(f" {ds['name']}: {ds['description']}")
105
+
106
+ # 加载 BTC 盘口快照(~13750 行)
107
+ ob = load_orderbook("BTC")
108
+ print(ob.columns.tolist())
109
+ # ['recorded_at_ts', 'market_slug', 'coin', 'up_bid', 'up_ask', ...]
110
+
111
+ # 加载 Flash Crash 策略交易记录
112
+ trades = load_trades("flash_crash")
113
+ print(trades[["coin", "side", "gross_pnl", "exit_reason"]].head())
114
+
115
+ # 加载 Hedge Arb 策略交易记录
116
+ hedge_trades = load_trades("hedge_arb")
117
+ ```
118
+
119
+ ### 4. 计算回测指标
120
+
121
+ ```python
122
+ from polymarket_backtest.backtest import summary, sharpe_ratio, max_drawdown, win_rate
123
+ from polymarket_backtest.data import load_trades
124
+
125
+ # 加载回测数据
126
+ trades = load_trades("flash_crash")
127
+ pnl = trades["gross_pnl"].dropna().tolist()
128
+
129
+ # 综合摘要
130
+ result = summary(pnl)
131
+ print(result)
132
+ # {
133
+ # 'total_trades': 22,
134
+ # 'net_pnl': 47.92,
135
+ # 'avg_pnl': 2.18,
136
+ # 'std_pnl': 5.67,
137
+ # 'win_rate': 0.143,
138
+ # 'profit_factor': 1.08,
139
+ # 'sharpe_ratio': 0.384,
140
+ # 'max_drawdown': 18.5,
141
+ # 'max_drawdown_pct': 22.3,
142
+ # 'calmar_ratio': 2.59
143
+ # }
144
+
145
+ # 单独计算各指标
146
+ print("Sharpe Ratio:", sharpe_ratio(pnl))
147
+ print("年化 Sharpe (15m 市场):", sharpe_ratio(pnl, periods_per_year=35040))
148
+
149
+ dd = max_drawdown(pnl)
150
+ print(f"最大回撤: {dd['max_drawdown']:.2f} USDC ({dd['max_drawdown_pct']:.1f}%)")
151
+
152
+ print("胜率:", win_rate(pnl))
153
+
154
+ # 直接从 DataFrame 计算
155
+ from polymarket_backtest.backtest import summary_from_df
156
+ result2 = summary_from_df(trades, pnl_col="gross_pnl")
157
+ ```
158
+
159
+ ## API 参考
160
+
161
+ ### `GammaClient`
162
+
163
+ | 方法 | 说明 |
164
+ |------|------|
165
+ | `get_market_info(coin)` | 获取当前活跃的 15 分钟市场信息 |
166
+ | `get_market_by_slug(slug)` | 通过 slug 精确查询 |
167
+ | `list_recent_markets(coin, n=10)` | 列出最近 n 个市场 |
168
+
169
+ ### `ClobClient`
170
+
171
+ | 方法 | 说明 |
172
+ |------|------|
173
+ | `get_price_history(token_id, interval, fidelity, ...)` | 拉取赔率历史,返回 `OddsHistory` |
174
+ | `get_price_history_df(token_id, ...)` | 同上,返回 `pd.DataFrame` |
175
+
176
+ **interval 参数**:`"1m"` / `"1h"` / `"6h"` / `"1d"` / `"1w"` / `"max"`
177
+
178
+ ### 内置数据集
179
+
180
+ | 名称 | 描述 |
181
+ |------|------|
182
+ | `btc_orderbook` | BTC 15 分钟盘口快照,~13750 行 |
183
+ | `flash_crash_trades` | Flash Crash 策略逐笔交易 |
184
+ | `flash_crash_summary` | Flash Crash 策略汇总统计 |
185
+ | `hedge_arb_trades` | Hedge Arbitrage 策略逐笔交易 |
186
+
187
+ ### 回测指标
188
+
189
+ | 函数 | 说明 |
190
+ |------|------|
191
+ | `sharpe_ratio(pnl, periods_per_year=None)` | 夏普比率,可选年化 |
192
+ | `max_drawdown(pnl)` | 最大回撤(金额 + 百分比) |
193
+ | `win_rate(pnl)` | 胜率 [0, 1] |
194
+ | `profit_factor(pnl)` | 盈亏比 |
195
+ | `calmar_ratio(pnl)` | 卡玛比率 |
196
+ | `summary(pnl, periods_per_year=None)` | 综合统计摘要 |
197
+ | `summary_from_df(df, pnl_col="net_pnl")` | 直接从 DataFrame 计算 |
198
+
199
+ ## 数据说明
200
+
201
+ 盘口数据字段说明:
202
+
203
+ | 字段 | 说明 |
204
+ |------|------|
205
+ | `recorded_at_ts` | UNIX 时间戳(秒) |
206
+ | `up_bid / up_ask / up_mid` | UP 方向的买/卖/中间价 |
207
+ | `down_bid / down_ask / down_mid` | DOWN 方向的买/卖/中间价 |
208
+ | `remaining_seconds` | 距市场结束的秒数 |
209
+ | `elapsed_seconds` | 市场已进行的秒数 |
210
+
211
+ ## 依赖
212
+
213
+ - Python >= 3.10
214
+ - `requests` >= 2.28
215
+ - `numpy` >= 1.24(可选,如未安装则用纯标准库计算)
216
+ - `pandas` >= 2.0
@@ -0,0 +1,188 @@
1
+ # polymarket-backtest
2
+
3
+ Polymarket API 封装与策略回测评估工具包。
4
+
5
+ ## 功能
6
+
7
+ - **API 封装**:Polymarket Gamma API(合约信息)、CLOB API(赔率历史时间序列)
8
+ - **内置数据集**:BTC 15 分钟市场盘口快照 + Flash Crash / Hedge Arb 策略回测记录
9
+ - **回测指标**:Sharpe Ratio、最大回撤、胜率、盈亏比、卡玛比率
10
+
11
+ ## 安装
12
+
13
+ ```bash
14
+ pip install -e /path/to/polymarket_backtest
15
+ # 或
16
+ cd /path/to/polymarket_backtest && pip install -e .
17
+ ```
18
+
19
+ ## 快速开始
20
+
21
+ ### 1. 查询合约信息
22
+
23
+ ```python
24
+ from polymarket_backtest.api import GammaClient
25
+
26
+ gamma = GammaClient()
27
+
28
+ # 获取当前活跃的 BTC 15 分钟市场
29
+ market = gamma.get_market_info("BTC")
30
+ print(market.slug) # "btc-updown-15m-1775035200"
31
+ print(market.up_price) # 0.52
32
+ print(market.down_price) # 0.48
33
+ print(market.up_token_id) # "229931..."
34
+
35
+ # 列出最近 5 个市场
36
+ markets = gamma.list_recent_markets("ETH", n=5)
37
+ for m in markets:
38
+ print(m.slug, m.end_date)
39
+ ```
40
+
41
+ ### 2. 拉取赔率历史
42
+
43
+ ```python
44
+ from polymarket_backtest.api import GammaClient, ClobClient
45
+
46
+ gamma = GammaClient()
47
+ clob = ClobClient()
48
+
49
+ market = gamma.get_market_info("BTC")
50
+
51
+ # 拉取最近 1 天数据(每小时 1 个点)
52
+ history = clob.get_price_history(
53
+ market.up_token_id,
54
+ interval="1d",
55
+ fidelity=60,
56
+ )
57
+ print(f"获取到 {len(history)} 个价格点")
58
+
59
+ for point in history.points[:3]:
60
+ print(point.timestamp, point.price)
61
+
62
+ # 直接返回 DataFrame
63
+ df = clob.get_price_history_df(market.up_token_id, interval="1w", fidelity=60)
64
+ print(df.head())
65
+ # timestamp price datetime
66
+ # 0 1697875200 0.520 2023-10-21 08:00:00+00:00
67
+ ```
68
+
69
+ ### 3. 加载内置数据集
70
+
71
+ ```python
72
+ from polymarket_backtest.data import list_datasets, load_orderbook, load_trades, load_summary
73
+
74
+ # 查看可用数据集
75
+ for ds in list_datasets():
76
+ print(f" {ds['name']}: {ds['description']}")
77
+
78
+ # 加载 BTC 盘口快照(~13750 行)
79
+ ob = load_orderbook("BTC")
80
+ print(ob.columns.tolist())
81
+ # ['recorded_at_ts', 'market_slug', 'coin', 'up_bid', 'up_ask', ...]
82
+
83
+ # 加载 Flash Crash 策略交易记录
84
+ trades = load_trades("flash_crash")
85
+ print(trades[["coin", "side", "gross_pnl", "exit_reason"]].head())
86
+
87
+ # 加载 Hedge Arb 策略交易记录
88
+ hedge_trades = load_trades("hedge_arb")
89
+ ```
90
+
91
+ ### 4. 计算回测指标
92
+
93
+ ```python
94
+ from polymarket_backtest.backtest import summary, sharpe_ratio, max_drawdown, win_rate
95
+ from polymarket_backtest.data import load_trades
96
+
97
+ # 加载回测数据
98
+ trades = load_trades("flash_crash")
99
+ pnl = trades["gross_pnl"].dropna().tolist()
100
+
101
+ # 综合摘要
102
+ result = summary(pnl)
103
+ print(result)
104
+ # {
105
+ # 'total_trades': 22,
106
+ # 'net_pnl': 47.92,
107
+ # 'avg_pnl': 2.18,
108
+ # 'std_pnl': 5.67,
109
+ # 'win_rate': 0.143,
110
+ # 'profit_factor': 1.08,
111
+ # 'sharpe_ratio': 0.384,
112
+ # 'max_drawdown': 18.5,
113
+ # 'max_drawdown_pct': 22.3,
114
+ # 'calmar_ratio': 2.59
115
+ # }
116
+
117
+ # 单独计算各指标
118
+ print("Sharpe Ratio:", sharpe_ratio(pnl))
119
+ print("年化 Sharpe (15m 市场):", sharpe_ratio(pnl, periods_per_year=35040))
120
+
121
+ dd = max_drawdown(pnl)
122
+ print(f"最大回撤: {dd['max_drawdown']:.2f} USDC ({dd['max_drawdown_pct']:.1f}%)")
123
+
124
+ print("胜率:", win_rate(pnl))
125
+
126
+ # 直接从 DataFrame 计算
127
+ from polymarket_backtest.backtest import summary_from_df
128
+ result2 = summary_from_df(trades, pnl_col="gross_pnl")
129
+ ```
130
+
131
+ ## API 参考
132
+
133
+ ### `GammaClient`
134
+
135
+ | 方法 | 说明 |
136
+ |------|------|
137
+ | `get_market_info(coin)` | 获取当前活跃的 15 分钟市场信息 |
138
+ | `get_market_by_slug(slug)` | 通过 slug 精确查询 |
139
+ | `list_recent_markets(coin, n=10)` | 列出最近 n 个市场 |
140
+
141
+ ### `ClobClient`
142
+
143
+ | 方法 | 说明 |
144
+ |------|------|
145
+ | `get_price_history(token_id, interval, fidelity, ...)` | 拉取赔率历史,返回 `OddsHistory` |
146
+ | `get_price_history_df(token_id, ...)` | 同上,返回 `pd.DataFrame` |
147
+
148
+ **interval 参数**:`"1m"` / `"1h"` / `"6h"` / `"1d"` / `"1w"` / `"max"`
149
+
150
+ ### 内置数据集
151
+
152
+ | 名称 | 描述 |
153
+ |------|------|
154
+ | `btc_orderbook` | BTC 15 分钟盘口快照,~13750 行 |
155
+ | `flash_crash_trades` | Flash Crash 策略逐笔交易 |
156
+ | `flash_crash_summary` | Flash Crash 策略汇总统计 |
157
+ | `hedge_arb_trades` | Hedge Arbitrage 策略逐笔交易 |
158
+
159
+ ### 回测指标
160
+
161
+ | 函数 | 说明 |
162
+ |------|------|
163
+ | `sharpe_ratio(pnl, periods_per_year=None)` | 夏普比率,可选年化 |
164
+ | `max_drawdown(pnl)` | 最大回撤(金额 + 百分比) |
165
+ | `win_rate(pnl)` | 胜率 [0, 1] |
166
+ | `profit_factor(pnl)` | 盈亏比 |
167
+ | `calmar_ratio(pnl)` | 卡玛比率 |
168
+ | `summary(pnl, periods_per_year=None)` | 综合统计摘要 |
169
+ | `summary_from_df(df, pnl_col="net_pnl")` | 直接从 DataFrame 计算 |
170
+
171
+ ## 数据说明
172
+
173
+ 盘口数据字段说明:
174
+
175
+ | 字段 | 说明 |
176
+ |------|------|
177
+ | `recorded_at_ts` | UNIX 时间戳(秒) |
178
+ | `up_bid / up_ask / up_mid` | UP 方向的买/卖/中间价 |
179
+ | `down_bid / down_ask / down_mid` | DOWN 方向的买/卖/中间价 |
180
+ | `remaining_seconds` | 距市场结束的秒数 |
181
+ | `elapsed_seconds` | 市场已进行的秒数 |
182
+
183
+ ## 依赖
184
+
185
+ - Python >= 3.10
186
+ - `requests` >= 2.28
187
+ - `numpy` >= 1.24(可选,如未安装则用纯标准库计算)
188
+ - `pandas` >= 2.0
@@ -0,0 +1,35 @@
1
+ """
2
+ polymarket-backtest
3
+ ===================
4
+
5
+ Polymarket API 封装与策略回测评估工具包。
6
+
7
+ 快速开始:
8
+ # 1. 查询市场信息
9
+ from polymarket_backtest.api import GammaClient, ClobClient
10
+
11
+ gamma = GammaClient()
12
+ market = gamma.get_market_info("BTC")
13
+ print(market.slug, market.up_price)
14
+
15
+ # 2. 拉取赔率历史
16
+ clob = ClobClient()
17
+ history = clob.get_price_history(market.up_token_id, interval="1d")
18
+ print(f"{len(history)} 个价格点")
19
+
20
+ # 3. 加载内置示例数据集
21
+ from polymarket_backtest.data import load_trades, load_orderbook
22
+
23
+ trades = load_trades("flash_crash")
24
+ ob = load_orderbook("BTC")
25
+
26
+ # 4. 计算回测指标
27
+ from polymarket_backtest.backtest import summary
28
+
29
+ pnl = trades["gross_pnl"].dropna().tolist()
30
+ result = summary(pnl)
31
+ print(result)
32
+ # {'net_pnl': 47.92, 'win_rate': 0.143, 'sharpe_ratio': 0.85, 'max_drawdown': 12.5, ...}
33
+ """
34
+
35
+ __version__ = "0.1.0"
@@ -0,0 +1,13 @@
1
+ """Polymarket API 封装模块。"""
2
+
3
+ from .clob import ClobClient
4
+ from .gamma import GammaClient
5
+ from .models import MarketInfo, OddsHistory, PricePoint
6
+
7
+ __all__ = [
8
+ "GammaClient",
9
+ "ClobClient",
10
+ "MarketInfo",
11
+ "PricePoint",
12
+ "OddsHistory",
13
+ ]
@@ -0,0 +1,136 @@
1
+ """
2
+ Polymarket CLOB API 客户端(只读)
3
+
4
+ 封装赔率历史数据查询。无需认证。
5
+
6
+ 用法示例:
7
+ from polymarket_backtest.api import GammaClient, ClobClient
8
+
9
+ gamma = GammaClient()
10
+ clob = ClobClient()
11
+
12
+ market = gamma.get_market_info("BTC")
13
+ history = clob.get_price_history(market.up_token_id, interval="1d")
14
+
15
+ for point in history.points:
16
+ print(point.timestamp, point.price)
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from typing import Literal
22
+
23
+ import requests
24
+
25
+ from .models import OddsHistory, PricePoint
26
+
27
+ Interval = Literal["1m", "1h", "6h", "1d", "1w", "max"]
28
+
29
+
30
+ class ClobClient:
31
+ """
32
+ Polymarket CLOB API 客户端(只读子集)。
33
+
34
+ 提供赔率历史查询,无需 API 密钥。
35
+ """
36
+
37
+ BASE_URL = "https://clob.polymarket.com"
38
+
39
+ def __init__(self, base_url: str = BASE_URL, timeout: int = 15):
40
+ self.base_url = base_url.rstrip("/")
41
+ self.timeout = timeout
42
+ self._session = requests.Session()
43
+
44
+ # ------------------------------------------------------------------
45
+ # 公开接口
46
+ # ------------------------------------------------------------------
47
+
48
+ def get_price_history(
49
+ self,
50
+ token_id: str,
51
+ interval: Interval | None = "1d",
52
+ fidelity: int = 60,
53
+ start_ts: int | None = None,
54
+ end_ts: int | None = None,
55
+ ) -> OddsHistory:
56
+ """
57
+ 拉取某 token 的赔率历史时间序列。
58
+
59
+ 参数:
60
+ token_id: CLOB token ID(从 GammaClient 获取)
61
+ interval: 时间窗口("1m","1h","6h","1d","1w","max")
62
+ 与 start_ts/end_ts 互斥
63
+ fidelity: 数据分辨率(分钟),例如 60 = 每小时一个点
64
+ start_ts: 开始时间 Unix 时间戳(UTC)
65
+ end_ts: 结束时间 Unix 时间戳(UTC)
66
+
67
+ 返回:
68
+ OddsHistory 对象,包含时间戳和对应价格列表
69
+
70
+ 示例:
71
+ # 拉取最近 1 天数据,每小时一个点
72
+ history = clob.get_price_history(token_id, interval="1d", fidelity=60)
73
+
74
+ # 拉取指定时间范围
75
+ history = clob.get_price_history(
76
+ token_id,
77
+ start_ts=1697875200,
78
+ end_ts=1697961600,
79
+ fidelity=5,
80
+ )
81
+ """
82
+ params: dict = {"market": token_id, "fidelity": fidelity}
83
+
84
+ if start_ts is not None or end_ts is not None:
85
+ if start_ts is not None:
86
+ params["startTs"] = start_ts
87
+ if end_ts is not None:
88
+ params["endTs"] = end_ts
89
+ elif interval is not None:
90
+ params["interval"] = interval
91
+
92
+ url = f"{self.base_url}/prices-history"
93
+ resp = self._session.get(url, params=params, timeout=self.timeout)
94
+ resp.raise_for_status()
95
+
96
+ data = resp.json()
97
+ raw_points = data.get("history", [])
98
+ points = [PricePoint(timestamp=int(p["t"]), price=float(p["p"])) for p in raw_points]
99
+
100
+ return OddsHistory(
101
+ token_id=token_id,
102
+ interval=interval or "custom",
103
+ fidelity=fidelity,
104
+ points=points,
105
+ )
106
+
107
+ def get_price_history_df(
108
+ self,
109
+ token_id: str,
110
+ interval: Interval | None = "1d",
111
+ fidelity: int = 60,
112
+ start_ts: int | None = None,
113
+ end_ts: int | None = None,
114
+ ):
115
+ """
116
+ 同 get_price_history,但直接返回 pandas DataFrame。
117
+
118
+ DataFrame 列:timestamp(Unix)、price、datetime(UTC)
119
+ """
120
+ import pandas as pd
121
+
122
+ history = self.get_price_history(
123
+ token_id,
124
+ interval=interval,
125
+ fidelity=fidelity,
126
+ start_ts=start_ts,
127
+ end_ts=end_ts,
128
+ )
129
+ if not history.points:
130
+ return pd.DataFrame(columns=["timestamp", "price", "datetime"])
131
+
132
+ df = pd.DataFrame(
133
+ {"timestamp": history.timestamps(), "price": history.prices()}
134
+ )
135
+ df["datetime"] = pd.to_datetime(df["timestamp"], unit="s", utc=True)
136
+ return df