alphafeed 0.1.0.dev0__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.
@@ -0,0 +1,175 @@
1
+ Metadata-Version: 2.4
2
+ Name: alphafeed
3
+ Version: 0.1.0.dev0
4
+ Summary: AlphaFeed Python SDK
5
+ Author: AlphaFeed
6
+ License: MIT
7
+ Project-URL: Documentation, https://docs.alphafeed.org
8
+ Keywords: finance,stock,market-data,quant,kline,alphafeed,a-share,us-stock,hk-stock,real-time-quotes
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Intended Audience :: Financial and Insurance Industry
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Office/Business :: Financial :: Investment
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: httpx>=0.25.0
24
+ Requires-Dist: typing-extensions>=4.0.0
25
+ Requires-Dist: pandas>=1.5.0
26
+ Requires-Dist: tqdm>=4.60.0
27
+
28
+ # AlphaFeed Python SDK
29
+
30
+ AlphaFeed Python SDK 是 AlphaFeed 金融市场数据 API 的 Python 客户端,支持 A 股、ETF、美股、港股。
31
+
32
+ > **完整文档**:<https://docs.alphafeed.org>
33
+
34
+ ---
35
+
36
+ ## 安装
37
+
38
+ ```bash
39
+ pip install alphafeed
40
+ ```
41
+
42
+ SDK 支持 Python 3.9+,推荐 3.10 或更高版本。内置 pandas 和 tqdm 支持。
43
+
44
+ ---
45
+
46
+ ## 初始化
47
+
48
+ ```python
49
+ from alphafeed import AlphaFeed
50
+
51
+ af = AlphaFeed(api_key="your-api-key")
52
+ ```
53
+
54
+ 也支持环境变量:
55
+
56
+ ```bash
57
+ export ALPHAFEED_API_KEY="your-api-key"
58
+ ```
59
+
60
+ ```python
61
+ from alphafeed import AlphaFeed
62
+ af = AlphaFeed() # 自动读取 ALPHAFEED_API_KEY
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 标的代码格式
68
+
69
+ | 示例 | 说明 |
70
+ |------|------|
71
+ | `600000.SH` | 上交所 |
72
+ | `000001.SZ` | 深交所 |
73
+ | `AAPL.US` | 美股 |
74
+ | `00700.HK` | 港股 |
75
+
76
+ ---
77
+
78
+ ## 基础用法
79
+
80
+ ### K 线获取
81
+
82
+ ```python
83
+ from alphafeed import AlphaFeed
84
+
85
+ af = AlphaFeed(api_key="your-api-key")
86
+
87
+ # 日 K 线,返回 DataFrame
88
+ df = af.klines.get("600000.SH", period="1d", count=100, to_dataframe=True)
89
+ print(df.tail())
90
+
91
+ # 分钟 K 线
92
+ df = af.klines.get("600000.SH", period="5m", count=100, to_dataframe=True)
93
+
94
+ # 前复权(默认)
95
+ df = af.klines.get("600519.SH", adjust="forward", to_dataframe=True)
96
+ ```
97
+
98
+ **批量获取**:多只标的一次拉取:
99
+
100
+ ```python
101
+ symbols = ["600000.SH", "000001.SZ", "600519.SH"]
102
+ dfs = af.klines.batch(symbols, period="1d", count=100, to_dataframe=True, show_progress=True)
103
+ print(dfs["600000.SH"].tail())
104
+ ```
105
+
106
+ ### 日内分时
107
+
108
+ ```python
109
+ # 当日分钟线
110
+ df = af.klines.intraday("600000.SH", to_dataframe=True)
111
+ print(df.tail())
112
+
113
+ # 批量日内分时
114
+ dfs = af.klines.intraday_batch(["600000.SH", "000001.SZ"], to_dataframe=True)
115
+ ```
116
+
117
+ ### 实时行情
118
+
119
+ **按标的代码查询**
120
+
121
+ ```python
122
+ quotes = af.quotes.get(symbols=["600000.SH", "000001.SZ", "00700.HK"], to_dataframe=True)
123
+ print(quotes)
124
+ ```
125
+
126
+ **按标的池查询**
127
+
128
+ ```python
129
+ # 全部 A 股行情
130
+ quotes_a = af.quotes.get(universes=["CN_Stock"], to_dataframe=True)
131
+ print(quotes_a)
132
+
133
+ # 全部 ETF 行情
134
+ quotes_etf = af.quotes.get(universes=["CN_ETF"], to_dataframe=True)
135
+ print(quotes_etf)
136
+ ```
137
+
138
+ 支持的标的池:`CN_Stock`(A股)、`US_Stock`(美股)、`HK_Stock`(港股)、`CN_ETF`(ETF)
139
+
140
+ ### 五档盘口
141
+
142
+ ```python
143
+ depth = af.depth.get("600000.SH")
144
+ print("买盘:", depth["bids"][:5])
145
+ print("卖盘:", depth["asks"][:5])
146
+ ```
147
+
148
+ ### 标的信息
149
+
150
+ ```python
151
+ instruments = af.instruments.batch(symbols=["600000.SH", "000001.SZ", "00700.HK"])
152
+ for inst in instruments:
153
+ print(f"{inst['symbol']}: {inst['name']}")
154
+ ```
155
+
156
+ ### 除权因子
157
+
158
+ ```python
159
+ factors = af.klines.ex_factors(symbols=["600000.SH", "600519.SH"], to_dataframe=True)
160
+ print(factors.tail())
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 更多文档
166
+
167
+ - [Python SDK 快速开始](https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart)
168
+ - [使用示例](https://docs.alphafeed.org/zh-Hans/sdk/python-examples)
169
+ - [HTTP API 说明](https://docs.alphafeed.org/zh-Hans/api-reference/introduction)
170
+
171
+ ---
172
+
173
+ ## License
174
+
175
+ MIT
@@ -0,0 +1,148 @@
1
+ # AlphaFeed Python SDK
2
+
3
+ AlphaFeed Python SDK 是 AlphaFeed 金融市场数据 API 的 Python 客户端,支持 A 股、ETF、美股、港股。
4
+
5
+ > **完整文档**:<https://docs.alphafeed.org>
6
+
7
+ ---
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ pip install alphafeed
13
+ ```
14
+
15
+ SDK 支持 Python 3.9+,推荐 3.10 或更高版本。内置 pandas 和 tqdm 支持。
16
+
17
+ ---
18
+
19
+ ## 初始化
20
+
21
+ ```python
22
+ from alphafeed import AlphaFeed
23
+
24
+ af = AlphaFeed(api_key="your-api-key")
25
+ ```
26
+
27
+ 也支持环境变量:
28
+
29
+ ```bash
30
+ export ALPHAFEED_API_KEY="your-api-key"
31
+ ```
32
+
33
+ ```python
34
+ from alphafeed import AlphaFeed
35
+ af = AlphaFeed() # 自动读取 ALPHAFEED_API_KEY
36
+ ```
37
+
38
+ ---
39
+
40
+ ## 标的代码格式
41
+
42
+ | 示例 | 说明 |
43
+ |------|------|
44
+ | `600000.SH` | 上交所 |
45
+ | `000001.SZ` | 深交所 |
46
+ | `AAPL.US` | 美股 |
47
+ | `00700.HK` | 港股 |
48
+
49
+ ---
50
+
51
+ ## 基础用法
52
+
53
+ ### K 线获取
54
+
55
+ ```python
56
+ from alphafeed import AlphaFeed
57
+
58
+ af = AlphaFeed(api_key="your-api-key")
59
+
60
+ # 日 K 线,返回 DataFrame
61
+ df = af.klines.get("600000.SH", period="1d", count=100, to_dataframe=True)
62
+ print(df.tail())
63
+
64
+ # 分钟 K 线
65
+ df = af.klines.get("600000.SH", period="5m", count=100, to_dataframe=True)
66
+
67
+ # 前复权(默认)
68
+ df = af.klines.get("600519.SH", adjust="forward", to_dataframe=True)
69
+ ```
70
+
71
+ **批量获取**:多只标的一次拉取:
72
+
73
+ ```python
74
+ symbols = ["600000.SH", "000001.SZ", "600519.SH"]
75
+ dfs = af.klines.batch(symbols, period="1d", count=100, to_dataframe=True, show_progress=True)
76
+ print(dfs["600000.SH"].tail())
77
+ ```
78
+
79
+ ### 日内分时
80
+
81
+ ```python
82
+ # 当日分钟线
83
+ df = af.klines.intraday("600000.SH", to_dataframe=True)
84
+ print(df.tail())
85
+
86
+ # 批量日内分时
87
+ dfs = af.klines.intraday_batch(["600000.SH", "000001.SZ"], to_dataframe=True)
88
+ ```
89
+
90
+ ### 实时行情
91
+
92
+ **按标的代码查询**
93
+
94
+ ```python
95
+ quotes = af.quotes.get(symbols=["600000.SH", "000001.SZ", "00700.HK"], to_dataframe=True)
96
+ print(quotes)
97
+ ```
98
+
99
+ **按标的池查询**
100
+
101
+ ```python
102
+ # 全部 A 股行情
103
+ quotes_a = af.quotes.get(universes=["CN_Stock"], to_dataframe=True)
104
+ print(quotes_a)
105
+
106
+ # 全部 ETF 行情
107
+ quotes_etf = af.quotes.get(universes=["CN_ETF"], to_dataframe=True)
108
+ print(quotes_etf)
109
+ ```
110
+
111
+ 支持的标的池:`CN_Stock`(A股)、`US_Stock`(美股)、`HK_Stock`(港股)、`CN_ETF`(ETF)
112
+
113
+ ### 五档盘口
114
+
115
+ ```python
116
+ depth = af.depth.get("600000.SH")
117
+ print("买盘:", depth["bids"][:5])
118
+ print("卖盘:", depth["asks"][:5])
119
+ ```
120
+
121
+ ### 标的信息
122
+
123
+ ```python
124
+ instruments = af.instruments.batch(symbols=["600000.SH", "000001.SZ", "00700.HK"])
125
+ for inst in instruments:
126
+ print(f"{inst['symbol']}: {inst['name']}")
127
+ ```
128
+
129
+ ### 除权因子
130
+
131
+ ```python
132
+ factors = af.klines.ex_factors(symbols=["600000.SH", "600519.SH"], to_dataframe=True)
133
+ print(factors.tail())
134
+ ```
135
+
136
+ ---
137
+
138
+ ## 更多文档
139
+
140
+ - [Python SDK 快速开始](https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart)
141
+ - [使用示例](https://docs.alphafeed.org/zh-Hans/sdk/python-examples)
142
+ - [HTTP API 说明](https://docs.alphafeed.org/zh-Hans/api-reference/introduction)
143
+
144
+ ---
145
+
146
+ ## License
147
+
148
+ MIT
@@ -0,0 +1,53 @@
1
+ """AlphaFeed Python SDK - 高性能行情数据客户端。
2
+
3
+ 支持 A股、ETF、美股、港股的行情数据查询。
4
+
5
+ Examples
6
+ --------
7
+ >>> from alphafeed import AlphaFeed
8
+ >>> client = AlphaFeed(api_key="your-api-key")
9
+ >>> df = client.klines.get("600000.SH", to_dataframe=True)
10
+ >>> print(df.tail())
11
+ """
12
+
13
+ from .__version__ import __version__
14
+ from ._exceptions import (
15
+ AlphaFeedError,
16
+ APIError,
17
+ AuthenticationError,
18
+ BadRequestError,
19
+ ConnectionError,
20
+ InternalServerError,
21
+ NotFoundError,
22
+ PermissionError,
23
+ RateLimitError,
24
+ TimeoutError,
25
+ )
26
+ from .client import AlphaFeed
27
+ from .models import (
28
+ AdjustType,
29
+ CompactKlineData,
30
+ Instrument,
31
+ Period,
32
+ Quote,
33
+ )
34
+
35
+ __all__ = [
36
+ "__version__",
37
+ "AlphaFeed",
38
+ "AlphaFeedError",
39
+ "APIError",
40
+ "AuthenticationError",
41
+ "PermissionError",
42
+ "NotFoundError",
43
+ "BadRequestError",
44
+ "RateLimitError",
45
+ "InternalServerError",
46
+ "ConnectionError",
47
+ "TimeoutError",
48
+ "AdjustType",
49
+ "CompactKlineData",
50
+ "Instrument",
51
+ "Period",
52
+ "Quote",
53
+ ]
@@ -0,0 +1,3 @@
1
+ from importlib.metadata import version
2
+
3
+ __version__ = version(__package__)
@@ -0,0 +1,305 @@
1
+ """Base HTTP client implementation with retry support."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import random
7
+ import time
8
+ from typing import Any, Optional, Union
9
+
10
+ import httpx
11
+
12
+ from . import __version__
13
+ from ._exceptions import (
14
+ APIError,
15
+ ConnectionError,
16
+ InternalServerError,
17
+ RateLimitError,
18
+ TimeoutError,
19
+ raise_for_status,
20
+ )
21
+ from ._types import NOT_GIVEN, Headers, NotGiven, Query, Timeout
22
+
23
+ __all__ = ["SyncAPIClient"]
24
+
25
+ DEFAULT_BASE_URL = "https://api.alphafeed.org"
26
+ DEFAULT_TIMEOUT = 30.0
27
+ DEFAULT_MAX_RETRIES = 3
28
+
29
+
30
+ def _should_retry(exception: Exception) -> bool:
31
+ """Determine if an exception is retryable.
32
+
33
+ Parameters
34
+ ----------
35
+ exception : Exception
36
+ The exception to check.
37
+
38
+ Returns
39
+ -------
40
+ bool
41
+ True if the request should be retried.
42
+ """
43
+ if isinstance(exception, (ConnectionError, TimeoutError)):
44
+ return True
45
+
46
+ if isinstance(exception, (InternalServerError, RateLimitError)):
47
+ return True
48
+
49
+ return False
50
+
51
+
52
+ def _calculate_retry_delay(
53
+ attempt: int, base_delay: float = 1.0, max_delay: float = 30.0
54
+ ) -> float:
55
+ """Calculate exponential backoff delay with jitter.
56
+
57
+ Parameters
58
+ ----------
59
+ attempt : int
60
+ Current attempt number (0-indexed).
61
+ base_delay : float
62
+ Base delay in seconds.
63
+ max_delay : float
64
+ Maximum delay in seconds.
65
+
66
+ Returns
67
+ -------
68
+ float
69
+ Delay in seconds.
70
+ """
71
+ delay = base_delay * (2**attempt)
72
+ jitter = delay * 0.25 * (2 * random.random() - 1)
73
+ delay = delay + jitter
74
+ return min(delay, max_delay)
75
+
76
+
77
+ class BaseClient:
78
+ """Base class with shared configuration for API clients."""
79
+
80
+ def __init__(
81
+ self,
82
+ api_key: Optional[str] = None,
83
+ base_url: Optional[str] = None,
84
+ timeout: Timeout = DEFAULT_TIMEOUT,
85
+ max_retries: int = DEFAULT_MAX_RETRIES,
86
+ default_headers: Optional[Headers] = None,
87
+ ) -> None:
88
+ if api_key is None:
89
+ self.api_key = os.environ.get("ALPHAFEED_API_KEY")
90
+ else:
91
+ self.api_key = api_key
92
+
93
+ if not self.api_key:
94
+ effective_base_url = base_url or os.environ.get("ALPHAFEED_BASE_URL")
95
+ if effective_base_url is None or effective_base_url == DEFAULT_BASE_URL:
96
+ raise ValueError(
97
+ "API key is required. Pass `api_key` or set ALPHAFEED_API_KEY environment variable."
98
+ )
99
+
100
+ self.base_url = (
101
+ base_url or os.environ.get("ALPHAFEED_BASE_URL") or DEFAULT_BASE_URL
102
+ ).rstrip("/")
103
+ self.timeout = timeout
104
+ self.max_retries = max_retries
105
+ self._default_headers = dict(default_headers) if default_headers else {}
106
+
107
+ def _build_headers(self, extra_headers: Optional[Headers] = None) -> dict[str, str]:
108
+ """Build request headers with authentication."""
109
+ headers = {
110
+ "Content-Type": "application/json",
111
+ "Accept": "application/json",
112
+ "User-Agent": f"alphafeed-python/{__version__}",
113
+ **self._default_headers,
114
+ }
115
+ if self.api_key:
116
+ headers["x-api-key"] = self.api_key
117
+ if extra_headers:
118
+ headers.update(extra_headers)
119
+ return headers
120
+
121
+ def _build_url(self, path: str) -> str:
122
+ """Build full URL from path."""
123
+ return f"{self.base_url}{path}"
124
+
125
+
126
+ class SyncAPIClient(BaseClient):
127
+ """Synchronous HTTP client for AlphaFeed API with automatic retry.
128
+
129
+ Parameters
130
+ ----------
131
+ api_key : str, optional
132
+ API key for authentication. If not provided, reads from ALPHAFEED_API_KEY
133
+ environment variable.
134
+ base_url : str, optional
135
+ Base URL for the API. Defaults to https://api.alphafeed.org.
136
+ timeout : float, optional
137
+ Request timeout in seconds. Defaults to 30.0.
138
+ max_retries : int, optional
139
+ Maximum number of retry attempts for failed requests. Defaults to 3.
140
+ Retries occur on connection errors, timeouts, server errors (5xx),
141
+ and rate limits (429).
142
+ default_headers : dict, optional
143
+ Default headers to include in all requests.
144
+
145
+ Examples
146
+ --------
147
+ >>> client = SyncAPIClient(api_key="your-api-key")
148
+ >>> response = client.get("/v1/klines", params={"symbol": "600000.SH"})
149
+ """
150
+
151
+ def __init__(
152
+ self,
153
+ api_key: Optional[str] = None,
154
+ base_url: Optional[str] = None,
155
+ timeout: Timeout = DEFAULT_TIMEOUT,
156
+ max_retries: int = DEFAULT_MAX_RETRIES,
157
+ default_headers: Optional[Headers] = None,
158
+ ) -> None:
159
+ super().__init__(api_key, base_url, timeout, max_retries, default_headers)
160
+ self._client = httpx.Client(timeout=timeout)
161
+
162
+ def __enter__(self) -> "SyncAPIClient":
163
+ return self
164
+
165
+ def __exit__(self, *args: Any) -> None:
166
+ self.close()
167
+
168
+ def close(self) -> None:
169
+ """Close the underlying HTTP client."""
170
+ self._client.close()
171
+
172
+ def _request(
173
+ self,
174
+ method: str,
175
+ path: str,
176
+ *,
177
+ params: Optional[Query] = None,
178
+ json: Optional[dict[str, Any]] = None,
179
+ extra_headers: Optional[Headers] = None,
180
+ timeout: Union[Timeout, NotGiven] = NOT_GIVEN,
181
+ max_retries: Union[int, NotGiven] = NOT_GIVEN,
182
+ ) -> Any:
183
+ """Make an HTTP request with automatic retry on failures.
184
+
185
+ Parameters
186
+ ----------
187
+ method : str
188
+ HTTP method (GET, POST, etc.).
189
+ path : str
190
+ API endpoint path.
191
+ params : dict, optional
192
+ Query parameters.
193
+ json : dict, optional
194
+ JSON request body.
195
+ extra_headers : dict, optional
196
+ Additional headers for this request.
197
+ timeout : float, optional
198
+ Override timeout for this request.
199
+ max_retries : int, optional
200
+ Override max retries for this request.
201
+
202
+ Returns
203
+ -------
204
+ Any
205
+ Parsed JSON response.
206
+
207
+ Raises
208
+ ------
209
+ APIError
210
+ If the API returns an error response after all retries.
211
+ ConnectionError
212
+ If there's a network connection issue after all retries.
213
+ TimeoutError
214
+ If the request times out after all retries.
215
+ """
216
+ url = self._build_url(path)
217
+ headers = self._build_headers(extra_headers)
218
+ request_timeout = timeout if not isinstance(timeout, NotGiven) else self.timeout
219
+ retries = (
220
+ max_retries if not isinstance(max_retries, NotGiven) else self.max_retries
221
+ )
222
+
223
+ if params:
224
+ params = {k: v for k, v in params.items() if v is not None}
225
+
226
+ last_exception: Optional[Exception] = None
227
+
228
+ for attempt in range(retries + 1):
229
+ try:
230
+ response = self._client.request(
231
+ method,
232
+ url,
233
+ params=params,
234
+ json=json,
235
+ headers=headers,
236
+ timeout=request_timeout,
237
+ )
238
+
239
+ try:
240
+ response_body = response.json()
241
+ except Exception:
242
+ response_body = {"message": response.text, "code": "PARSE_ERROR"}
243
+
244
+ raise_for_status(response.status_code, response_body)
245
+
246
+ return response_body
247
+
248
+ except httpx.ConnectError as e:
249
+ last_exception = ConnectionError(f"Failed to connect to {url}: {e}")
250
+ except httpx.TimeoutException as e:
251
+ last_exception = TimeoutError(f"Request to {url} timed out")
252
+ except APIError as e:
253
+ last_exception = e
254
+ if not _should_retry(e):
255
+ raise
256
+
257
+ if attempt < retries and _should_retry(last_exception):
258
+ delay = _calculate_retry_delay(attempt)
259
+ time.sleep(delay)
260
+ else:
261
+ break
262
+
263
+ if last_exception:
264
+ raise last_exception
265
+ raise RuntimeError("Unexpected state: no exception but request failed")
266
+
267
+ def get(
268
+ self,
269
+ path: str,
270
+ *,
271
+ params: Optional[Query] = None,
272
+ extra_headers: Optional[Headers] = None,
273
+ timeout: Union[Timeout, NotGiven] = NOT_GIVEN,
274
+ max_retries: Union[int, NotGiven] = NOT_GIVEN,
275
+ ) -> Any:
276
+ """Make a GET request with automatic retry."""
277
+ return self._request(
278
+ "GET",
279
+ path,
280
+ params=params,
281
+ extra_headers=extra_headers,
282
+ timeout=timeout,
283
+ max_retries=max_retries,
284
+ )
285
+
286
+ def post(
287
+ self,
288
+ path: str,
289
+ *,
290
+ json: Optional[dict[str, Any]] = None,
291
+ params: Optional[Query] = None,
292
+ extra_headers: Optional[Headers] = None,
293
+ timeout: Union[Timeout, NotGiven] = NOT_GIVEN,
294
+ max_retries: Union[int, NotGiven] = NOT_GIVEN,
295
+ ) -> Any:
296
+ """Make a POST request with automatic retry."""
297
+ return self._request(
298
+ "POST",
299
+ path,
300
+ json=json,
301
+ params=params,
302
+ extra_headers=extra_headers,
303
+ timeout=timeout,
304
+ max_retries=max_retries,
305
+ )