tickflow 0.1.0__py3-none-any.whl
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.
- tickflow/__init__.py +136 -0
- tickflow/_base_client.py +353 -0
- tickflow/_exceptions.py +140 -0
- tickflow/_types.py +60 -0
- tickflow/client.py +264 -0
- tickflow/generated_model.py +261 -0
- tickflow/resources/__init__.py +20 -0
- tickflow/resources/_base.py +29 -0
- tickflow/resources/exchanges.py +116 -0
- tickflow/resources/klines.py +460 -0
- tickflow/resources/quotes.py +397 -0
- tickflow/resources/symbols.py +176 -0
- tickflow/resources/universes.py +138 -0
- tickflow-0.1.0.dist-info/METADATA +36 -0
- tickflow-0.1.0.dist-info/RECORD +17 -0
- tickflow-0.1.0.dist-info/WHEEL +5 -0
- tickflow-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
"""Real-time quote resources for TickFlow API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Union, overload
|
|
6
|
+
|
|
7
|
+
from .._types import NOT_GIVEN, NotGiven
|
|
8
|
+
from ._base import AsyncResource, SyncResource
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
import pandas as pd
|
|
12
|
+
|
|
13
|
+
from ..generated_model import Quote
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _quotes_to_dataframe(data: List["Quote"]) -> "pd.DataFrame":
|
|
17
|
+
"""Convert quote data to a pandas DataFrame.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
data : list of Quote
|
|
22
|
+
List of quote dictionaries.
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
pd.DataFrame
|
|
27
|
+
DataFrame with symbol as index.
|
|
28
|
+
"""
|
|
29
|
+
import pandas as pd
|
|
30
|
+
|
|
31
|
+
if not data:
|
|
32
|
+
return pd.DataFrame()
|
|
33
|
+
|
|
34
|
+
df = pd.DataFrame(data)
|
|
35
|
+
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
|
|
36
|
+
df.set_index("symbol", inplace=True)
|
|
37
|
+
|
|
38
|
+
return df
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Quotes(SyncResource):
|
|
42
|
+
"""Synchronous interface for real-time quote endpoints.
|
|
43
|
+
|
|
44
|
+
Supports querying quotes by symbol codes or universe IDs.
|
|
45
|
+
|
|
46
|
+
Examples
|
|
47
|
+
--------
|
|
48
|
+
>>> client = TickFlow(api_key="your-key")
|
|
49
|
+
>>>
|
|
50
|
+
>>> # Get quotes by symbols
|
|
51
|
+
>>> quotes = client.quotes.get(symbols=["600000.SH", "AAPL.US"])
|
|
52
|
+
>>>
|
|
53
|
+
>>> # Get quotes by universe
|
|
54
|
+
>>> quotes = client.quotes.get(universes=["CN_Equity_A"])
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
@overload
|
|
58
|
+
def get(
|
|
59
|
+
self,
|
|
60
|
+
*,
|
|
61
|
+
symbols: Union[List[str], str, None] = None,
|
|
62
|
+
universes: Union[List[str], str, None] = None,
|
|
63
|
+
as_dataframe: Literal[False] = False,
|
|
64
|
+
) -> List["Quote"]: ...
|
|
65
|
+
|
|
66
|
+
@overload
|
|
67
|
+
def get(
|
|
68
|
+
self,
|
|
69
|
+
*,
|
|
70
|
+
symbols: Union[List[str], str, None] = None,
|
|
71
|
+
universes: Union[List[str], str, None] = None,
|
|
72
|
+
as_dataframe: Literal[True],
|
|
73
|
+
) -> "pd.DataFrame": ...
|
|
74
|
+
|
|
75
|
+
def get(
|
|
76
|
+
self,
|
|
77
|
+
*,
|
|
78
|
+
symbols: Union[List[str], str, None] = None,
|
|
79
|
+
universes: Union[List[str], str, None] = None,
|
|
80
|
+
as_dataframe: bool = False,
|
|
81
|
+
) -> Union[List["Quote"], "pd.DataFrame"]:
|
|
82
|
+
"""Get real-time quotes for symbols or universes.
|
|
83
|
+
|
|
84
|
+
Must provide either `symbols` or `universes`, but not both.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
symbols : str or list of str, optional
|
|
89
|
+
Symbol code(s) to query. Can be a single symbol, comma-separated
|
|
90
|
+
string, or list of symbols.
|
|
91
|
+
universes : str or list of str, optional
|
|
92
|
+
Universe ID(s) to query. Can be a single ID, comma-separated
|
|
93
|
+
string, or list of IDs.
|
|
94
|
+
as_dataframe : bool, optional
|
|
95
|
+
If True, return a pandas DataFrame indexed by symbol.
|
|
96
|
+
If False (default), return a list of Quote dicts.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
list of Quote or pd.DataFrame
|
|
101
|
+
Quote data for the requested symbols.
|
|
102
|
+
|
|
103
|
+
Each Quote contains:
|
|
104
|
+
- symbol: Symbol code
|
|
105
|
+
- name: Symbol name
|
|
106
|
+
- region: Region code
|
|
107
|
+
- last_price: Latest price
|
|
108
|
+
- prev_close: Previous close
|
|
109
|
+
- open, high, low: OHLC prices
|
|
110
|
+
- volume: Trading volume
|
|
111
|
+
- amount: Trading amount
|
|
112
|
+
- timestamp: Quote timestamp (milliseconds)
|
|
113
|
+
- session: Trading session status
|
|
114
|
+
- ext: Market-specific extension data
|
|
115
|
+
|
|
116
|
+
Raises
|
|
117
|
+
------
|
|
118
|
+
ValueError
|
|
119
|
+
If neither or both of `symbols` and `universes` are provided.
|
|
120
|
+
|
|
121
|
+
Examples
|
|
122
|
+
--------
|
|
123
|
+
>>> # Query by symbols
|
|
124
|
+
>>> quotes = client.quotes.get(symbols=["600000.SH", "AAPL.US"])
|
|
125
|
+
>>> for q in quotes:
|
|
126
|
+
... print(f"{q['symbol']}: {q['last_price']}")
|
|
127
|
+
|
|
128
|
+
>>> # Query by universe, as DataFrame
|
|
129
|
+
>>> df = client.quotes.get(universes="CN_Equity_A", as_dataframe=True)
|
|
130
|
+
>>> print(df[["last_price", "volume"]].head())
|
|
131
|
+
|
|
132
|
+
>>> # Find top gainers
|
|
133
|
+
>>> df["change_pct"] = (df["last_price"] - df["prev_close"]) / df["prev_close"] * 100
|
|
134
|
+
>>> top_gainers = df.nlargest(10, "change_pct")
|
|
135
|
+
"""
|
|
136
|
+
if (symbols is None) == (universes is None):
|
|
137
|
+
raise ValueError(
|
|
138
|
+
"Must provide either 'symbols' or 'universes', but not both"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Determine whether to use GET (small query) or POST (large query)
|
|
142
|
+
if symbols is not None:
|
|
143
|
+
if isinstance(symbols, str):
|
|
144
|
+
symbols_list = [s.strip() for s in symbols.split(",")]
|
|
145
|
+
else:
|
|
146
|
+
symbols_list = symbols
|
|
147
|
+
|
|
148
|
+
if len(symbols_list) <= 20:
|
|
149
|
+
# Use GET for small queries
|
|
150
|
+
response = self._client.get(
|
|
151
|
+
"/v1/quotes", params={"symbols": ",".join(symbols_list)}
|
|
152
|
+
)
|
|
153
|
+
else:
|
|
154
|
+
# Use POST for large queries
|
|
155
|
+
response = self._client.post(
|
|
156
|
+
"/v1/quotes", json={"symbols": symbols_list}
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
# Universe query
|
|
160
|
+
if isinstance(universes, str):
|
|
161
|
+
universes_list = [u.strip() for u in universes.split(",")]
|
|
162
|
+
else:
|
|
163
|
+
universes_list = universes
|
|
164
|
+
|
|
165
|
+
if len(universes_list) <= 5:
|
|
166
|
+
response = self._client.get(
|
|
167
|
+
"/v1/quotes", params={"universes": ",".join(universes_list)}
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
response = self._client.post(
|
|
171
|
+
"/v1/quotes", json={"universes": universes_list}
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
data = response["data"]
|
|
175
|
+
|
|
176
|
+
if as_dataframe:
|
|
177
|
+
return _quotes_to_dataframe(data)
|
|
178
|
+
return data
|
|
179
|
+
|
|
180
|
+
def get_by_symbols(
|
|
181
|
+
self,
|
|
182
|
+
symbols: List[str],
|
|
183
|
+
*,
|
|
184
|
+
as_dataframe: bool = False,
|
|
185
|
+
) -> Union[List["Quote"], "pd.DataFrame"]:
|
|
186
|
+
"""Get real-time quotes for a list of symbols.
|
|
187
|
+
|
|
188
|
+
This is a convenience method that always uses POST for batch queries.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
symbols : list of str
|
|
193
|
+
List of symbol codes.
|
|
194
|
+
as_dataframe : bool, optional
|
|
195
|
+
If True, return a pandas DataFrame.
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
list of Quote or pd.DataFrame
|
|
200
|
+
Quote data for the requested symbols.
|
|
201
|
+
|
|
202
|
+
Examples
|
|
203
|
+
--------
|
|
204
|
+
>>> quotes = client.quotes.get_by_symbols(["600000.SH", "000001.SZ", "AAPL.US"])
|
|
205
|
+
"""
|
|
206
|
+
response = self._client.post("/v1/quotes", json={"symbols": symbols})
|
|
207
|
+
data = response["data"]
|
|
208
|
+
|
|
209
|
+
if as_dataframe:
|
|
210
|
+
return _quotes_to_dataframe(data)
|
|
211
|
+
return data
|
|
212
|
+
|
|
213
|
+
def get_by_universes(
|
|
214
|
+
self,
|
|
215
|
+
universes: List[str],
|
|
216
|
+
*,
|
|
217
|
+
as_dataframe: bool = False,
|
|
218
|
+
) -> Union[List["Quote"], "pd.DataFrame"]:
|
|
219
|
+
"""Get real-time quotes for all symbols in the specified universes.
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
universes : list of str
|
|
224
|
+
List of universe IDs.
|
|
225
|
+
as_dataframe : bool, optional
|
|
226
|
+
If True, return a pandas DataFrame.
|
|
227
|
+
|
|
228
|
+
Returns
|
|
229
|
+
-------
|
|
230
|
+
list of Quote or pd.DataFrame
|
|
231
|
+
Quote data for all symbols in the universes.
|
|
232
|
+
|
|
233
|
+
Examples
|
|
234
|
+
--------
|
|
235
|
+
>>> # Get all A-share quotes
|
|
236
|
+
>>> quotes = client.quotes.get_by_universes(["CN_Equity_A"], as_dataframe=True)
|
|
237
|
+
"""
|
|
238
|
+
response = self._client.post("/v1/quotes", json={"universes": universes})
|
|
239
|
+
data = response["data"]
|
|
240
|
+
|
|
241
|
+
if as_dataframe:
|
|
242
|
+
return _quotes_to_dataframe(data)
|
|
243
|
+
return data
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class AsyncQuotes(AsyncResource):
|
|
247
|
+
"""Asynchronous interface for real-time quote endpoints.
|
|
248
|
+
|
|
249
|
+
Supports querying quotes by symbol codes or universe IDs.
|
|
250
|
+
|
|
251
|
+
Examples
|
|
252
|
+
--------
|
|
253
|
+
>>> async with AsyncTickFlow(api_key="your-key") as client:
|
|
254
|
+
... quotes = await client.quotes.get(symbols=["600000.SH", "AAPL.US"])
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
@overload
|
|
258
|
+
async def get(
|
|
259
|
+
self,
|
|
260
|
+
*,
|
|
261
|
+
symbols: Union[List[str], str, None] = None,
|
|
262
|
+
universes: Union[List[str], str, None] = None,
|
|
263
|
+
as_dataframe: Literal[False] = False,
|
|
264
|
+
) -> List["Quote"]: ...
|
|
265
|
+
|
|
266
|
+
@overload
|
|
267
|
+
async def get(
|
|
268
|
+
self,
|
|
269
|
+
*,
|
|
270
|
+
symbols: Union[List[str], str, None] = None,
|
|
271
|
+
universes: Union[List[str], str, None] = None,
|
|
272
|
+
as_dataframe: Literal[True],
|
|
273
|
+
) -> "pd.DataFrame": ...
|
|
274
|
+
|
|
275
|
+
async def get(
|
|
276
|
+
self,
|
|
277
|
+
*,
|
|
278
|
+
symbols: Union[List[str], str, None] = None,
|
|
279
|
+
universes: Union[List[str], str, None] = None,
|
|
280
|
+
as_dataframe: bool = False,
|
|
281
|
+
) -> Union[List["Quote"], "pd.DataFrame"]:
|
|
282
|
+
"""Get real-time quotes for symbols or universes.
|
|
283
|
+
|
|
284
|
+
Must provide either `symbols` or `universes`, but not both.
|
|
285
|
+
|
|
286
|
+
Parameters
|
|
287
|
+
----------
|
|
288
|
+
symbols : str or list of str, optional
|
|
289
|
+
Symbol code(s) to query.
|
|
290
|
+
universes : str or list of str, optional
|
|
291
|
+
Universe ID(s) to query.
|
|
292
|
+
as_dataframe : bool, optional
|
|
293
|
+
If True, return a pandas DataFrame indexed by symbol.
|
|
294
|
+
|
|
295
|
+
Returns
|
|
296
|
+
-------
|
|
297
|
+
list of Quote or pd.DataFrame
|
|
298
|
+
Quote data for the requested symbols.
|
|
299
|
+
|
|
300
|
+
Examples
|
|
301
|
+
--------
|
|
302
|
+
>>> quotes = await client.quotes.get(symbols=["600000.SH", "AAPL.US"])
|
|
303
|
+
>>> df = await client.quotes.get(universes="CN_Equity_A", as_dataframe=True)
|
|
304
|
+
"""
|
|
305
|
+
if (symbols is None) == (universes is None):
|
|
306
|
+
raise ValueError(
|
|
307
|
+
"Must provide either 'symbols' or 'universes', but not both"
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
if symbols is not None:
|
|
311
|
+
if isinstance(symbols, str):
|
|
312
|
+
symbols_list = [s.strip() for s in symbols.split(",")]
|
|
313
|
+
else:
|
|
314
|
+
symbols_list = symbols
|
|
315
|
+
|
|
316
|
+
if len(symbols_list) <= 20:
|
|
317
|
+
response = await self._client.get(
|
|
318
|
+
"/v1/quotes", params={"symbols": ",".join(symbols_list)}
|
|
319
|
+
)
|
|
320
|
+
else:
|
|
321
|
+
response = await self._client.post(
|
|
322
|
+
"/v1/quotes", json={"symbols": symbols_list}
|
|
323
|
+
)
|
|
324
|
+
else:
|
|
325
|
+
if isinstance(universes, str):
|
|
326
|
+
universes_list = [u.strip() for u in universes.split(",")]
|
|
327
|
+
else:
|
|
328
|
+
universes_list = universes
|
|
329
|
+
|
|
330
|
+
if len(universes_list) <= 5:
|
|
331
|
+
response = await self._client.get(
|
|
332
|
+
"/v1/quotes", params={"universes": ",".join(universes_list)}
|
|
333
|
+
)
|
|
334
|
+
else:
|
|
335
|
+
response = await self._client.post(
|
|
336
|
+
"/v1/quotes", json={"universes": universes_list}
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
data = response["data"]
|
|
340
|
+
|
|
341
|
+
if as_dataframe:
|
|
342
|
+
return _quotes_to_dataframe(data)
|
|
343
|
+
return data
|
|
344
|
+
|
|
345
|
+
async def get_by_symbols(
|
|
346
|
+
self,
|
|
347
|
+
symbols: List[str],
|
|
348
|
+
*,
|
|
349
|
+
as_dataframe: bool = False,
|
|
350
|
+
) -> Union[List["Quote"], "pd.DataFrame"]:
|
|
351
|
+
"""Get real-time quotes for a list of symbols.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
symbols : list of str
|
|
356
|
+
List of symbol codes.
|
|
357
|
+
as_dataframe : bool, optional
|
|
358
|
+
If True, return a pandas DataFrame.
|
|
359
|
+
|
|
360
|
+
Returns
|
|
361
|
+
-------
|
|
362
|
+
list of Quote or pd.DataFrame
|
|
363
|
+
Quote data for the requested symbols.
|
|
364
|
+
"""
|
|
365
|
+
response = await self._client.post("/v1/quotes", json={"symbols": symbols})
|
|
366
|
+
data = response["data"]
|
|
367
|
+
|
|
368
|
+
if as_dataframe:
|
|
369
|
+
return _quotes_to_dataframe(data)
|
|
370
|
+
return data
|
|
371
|
+
|
|
372
|
+
async def get_by_universes(
|
|
373
|
+
self,
|
|
374
|
+
universes: List[str],
|
|
375
|
+
*,
|
|
376
|
+
as_dataframe: bool = False,
|
|
377
|
+
) -> Union[List["Quote"], "pd.DataFrame"]:
|
|
378
|
+
"""Get real-time quotes for all symbols in the specified universes.
|
|
379
|
+
|
|
380
|
+
Parameters
|
|
381
|
+
----------
|
|
382
|
+
universes : list of str
|
|
383
|
+
List of universe IDs.
|
|
384
|
+
as_dataframe : bool, optional
|
|
385
|
+
If True, return a pandas DataFrame.
|
|
386
|
+
|
|
387
|
+
Returns
|
|
388
|
+
-------
|
|
389
|
+
list of Quote or pd.DataFrame
|
|
390
|
+
Quote data for all symbols in the universes.
|
|
391
|
+
"""
|
|
392
|
+
response = await self._client.post("/v1/quotes", json={"universes": universes})
|
|
393
|
+
data = response["data"]
|
|
394
|
+
|
|
395
|
+
if as_dataframe:
|
|
396
|
+
return _quotes_to_dataframe(data)
|
|
397
|
+
return data
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Symbol metadata resources for TickFlow API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, List, Union, overload
|
|
6
|
+
|
|
7
|
+
from ._base import AsyncResource, SyncResource
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ..generated_model import SymbolMeta
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Symbols(SyncResource):
|
|
14
|
+
"""Synchronous interface for symbol metadata endpoints.
|
|
15
|
+
|
|
16
|
+
Examples
|
|
17
|
+
--------
|
|
18
|
+
>>> client = TickFlow(api_key="your-key")
|
|
19
|
+
>>> meta = client.symbols.get("600000.SH")
|
|
20
|
+
>>> print(f"{meta['symbol']}: {meta['name']}")
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@overload
|
|
24
|
+
def get(self, symbol: str) -> "SymbolMeta": ...
|
|
25
|
+
|
|
26
|
+
@overload
|
|
27
|
+
def get(self, symbol: List[str]) -> List["SymbolMeta"]: ...
|
|
28
|
+
|
|
29
|
+
def get(
|
|
30
|
+
self, symbol: Union[str, List[str]]
|
|
31
|
+
) -> Union["SymbolMeta", List["SymbolMeta"]]:
|
|
32
|
+
"""Get metadata for one or more symbols.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
symbol : str or list of str
|
|
37
|
+
Symbol code(s). Can be a single symbol string or a list of symbols.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
SymbolMeta or list of SymbolMeta
|
|
42
|
+
If a single symbol is provided, returns a single SymbolMeta dict.
|
|
43
|
+
If a list is provided, returns a list of SymbolMeta dicts.
|
|
44
|
+
|
|
45
|
+
Each SymbolMeta contains:
|
|
46
|
+
- symbol: Full symbol code (e.g., "600000.SH")
|
|
47
|
+
- code: Exchange-specific code (e.g., "600000")
|
|
48
|
+
- exchange: Exchange code (e.g., "SH")
|
|
49
|
+
- region: Region code (e.g., "CN")
|
|
50
|
+
- name: Symbol name
|
|
51
|
+
- symbol_type: Type (stock, etf, index, etc.)
|
|
52
|
+
- ext: Market-specific extension data
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
>>> # Single symbol
|
|
57
|
+
>>> meta = client.symbols.get("600000.SH")
|
|
58
|
+
>>> print(meta['name'])
|
|
59
|
+
|
|
60
|
+
>>> # Multiple symbols
|
|
61
|
+
>>> metas = client.symbols.get(["600000.SH", "AAPL.US"])
|
|
62
|
+
>>> for m in metas:
|
|
63
|
+
... print(f"{m['symbol']}: {m['name']}")
|
|
64
|
+
"""
|
|
65
|
+
if isinstance(symbol, str):
|
|
66
|
+
response = self._client.get("/v1/symbols", params={"symbols": symbol})
|
|
67
|
+
data = response["data"]
|
|
68
|
+
return data[0] if data else {}
|
|
69
|
+
else:
|
|
70
|
+
# Use POST for batch queries
|
|
71
|
+
response = self._client.post("/v1/symbols", json={"symbols": symbol})
|
|
72
|
+
return response["data"]
|
|
73
|
+
|
|
74
|
+
def batch(self, symbols: List[str]) -> List["SymbolMeta"]:
|
|
75
|
+
"""Get metadata for multiple symbols.
|
|
76
|
+
|
|
77
|
+
This method uses POST to handle large batches without URL length limits.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
symbols : list of str
|
|
82
|
+
List of symbol codes (up to 1000).
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
list of SymbolMeta
|
|
87
|
+
List of symbol metadata dicts.
|
|
88
|
+
|
|
89
|
+
Examples
|
|
90
|
+
--------
|
|
91
|
+
>>> metas = client.symbols.batch(["600000.SH", "000001.SZ", "AAPL.US"])
|
|
92
|
+
>>> for m in metas:
|
|
93
|
+
... print(f"{m['symbol']}: {m['name']}")
|
|
94
|
+
"""
|
|
95
|
+
response = self._client.post("/v1/symbols", json={"symbols": symbols})
|
|
96
|
+
return response["data"]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class AsyncSymbols(AsyncResource):
|
|
100
|
+
"""Asynchronous interface for symbol metadata endpoints.
|
|
101
|
+
|
|
102
|
+
Examples
|
|
103
|
+
--------
|
|
104
|
+
>>> async with AsyncTickFlow(api_key="your-key") as client:
|
|
105
|
+
... meta = await client.symbols.get("600000.SH")
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
@overload
|
|
109
|
+
async def get(self, symbol: str) -> "SymbolMeta": ...
|
|
110
|
+
|
|
111
|
+
@overload
|
|
112
|
+
async def get(self, symbol: List[str]) -> List["SymbolMeta"]: ...
|
|
113
|
+
|
|
114
|
+
async def get(
|
|
115
|
+
self, symbol: Union[str, List[str]]
|
|
116
|
+
) -> Union["SymbolMeta", List["SymbolMeta"]]:
|
|
117
|
+
"""Get metadata for one or more symbols.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
symbol : str or list of str
|
|
122
|
+
Symbol code(s). Can be a single symbol string or a list of symbols.
|
|
123
|
+
|
|
124
|
+
Returns
|
|
125
|
+
-------
|
|
126
|
+
SymbolMeta or list of SymbolMeta
|
|
127
|
+
If a single symbol is provided, returns a single SymbolMeta dict.
|
|
128
|
+
If a list is provided, returns a list of SymbolMeta dicts.
|
|
129
|
+
|
|
130
|
+
Each SymbolMeta contains:
|
|
131
|
+
- symbol: Full symbol code (e.g., "600000.SH")
|
|
132
|
+
- code: Exchange-specific code (e.g., "600000")
|
|
133
|
+
- exchange: Exchange code (e.g., "SH")
|
|
134
|
+
- region: Region code (e.g., "CN")
|
|
135
|
+
- name: Symbol name
|
|
136
|
+
- symbol_type: Type (stock, etf, index, etc.)
|
|
137
|
+
- ext: Market-specific extension data
|
|
138
|
+
|
|
139
|
+
Examples
|
|
140
|
+
--------
|
|
141
|
+
>>> # Single symbol
|
|
142
|
+
>>> meta = await client.symbols.get("600000.SH")
|
|
143
|
+
>>> print(meta['name'])
|
|
144
|
+
|
|
145
|
+
>>> # Multiple symbols
|
|
146
|
+
>>> metas = await client.symbols.get(["600000.SH", "AAPL.US"])
|
|
147
|
+
"""
|
|
148
|
+
if isinstance(symbol, str):
|
|
149
|
+
response = await self._client.get("/v1/symbols", params={"symbols": symbol})
|
|
150
|
+
data = response["data"]
|
|
151
|
+
return data[0] if data else {}
|
|
152
|
+
else:
|
|
153
|
+
response = await self._client.post("/v1/symbols", json={"symbols": symbol})
|
|
154
|
+
return response["data"]
|
|
155
|
+
|
|
156
|
+
async def batch(self, symbols: List[str]) -> List["SymbolMeta"]:
|
|
157
|
+
"""Get metadata for multiple symbols.
|
|
158
|
+
|
|
159
|
+
This method uses POST to handle large batches without URL length limits.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
symbols : list of str
|
|
164
|
+
List of symbol codes (up to 1000).
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
list of SymbolMeta
|
|
169
|
+
List of symbol metadata dicts.
|
|
170
|
+
|
|
171
|
+
Examples
|
|
172
|
+
--------
|
|
173
|
+
>>> metas = await client.symbols.batch(["600000.SH", "000001.SZ", "AAPL.US"])
|
|
174
|
+
"""
|
|
175
|
+
response = await self._client.post("/v1/symbols", json={"symbols": symbols})
|
|
176
|
+
return response["data"]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Universe resources for TickFlow API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, List
|
|
6
|
+
|
|
7
|
+
from ._base import AsyncResource, SyncResource
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ..generated_model import UniverseDetail, UniverseSummary
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Universes(SyncResource):
|
|
14
|
+
"""Synchronous interface for universe (symbol pool) endpoints.
|
|
15
|
+
|
|
16
|
+
Universes are predefined collections of symbols, such as "A-shares" or "US equities".
|
|
17
|
+
|
|
18
|
+
Examples
|
|
19
|
+
--------
|
|
20
|
+
>>> client = TickFlow(api_key="your-key")
|
|
21
|
+
>>> universes = client.universes.list()
|
|
22
|
+
>>> for u in universes:
|
|
23
|
+
... print(f"{u['id']}: {u['name']} ({u['symbol_count']} symbols)")
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def list(self) -> List["UniverseSummary"]:
|
|
27
|
+
"""Get list of all available universes.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
list of UniverseSummary
|
|
32
|
+
List of universe summaries containing:
|
|
33
|
+
- id: Unique identifier (e.g., "CN_Equity_A")
|
|
34
|
+
- name: Display name
|
|
35
|
+
- region: Region code
|
|
36
|
+
- category: Category (equity, etf, index, etc.)
|
|
37
|
+
- symbol_count: Number of symbols
|
|
38
|
+
- description: Optional description
|
|
39
|
+
|
|
40
|
+
Examples
|
|
41
|
+
--------
|
|
42
|
+
>>> universes = client.universes.list()
|
|
43
|
+
>>> cn_equity = next(u for u in universes if u['id'] == 'CN_Equity_A')
|
|
44
|
+
>>> print(f"A-shares: {cn_equity['symbol_count']} symbols")
|
|
45
|
+
"""
|
|
46
|
+
response = self._client.get("/v1/universes")
|
|
47
|
+
return response["data"]
|
|
48
|
+
|
|
49
|
+
def get(self, universe_id: str) -> "UniverseDetail":
|
|
50
|
+
"""Get detailed information for a specific universe.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
universe_id : str
|
|
55
|
+
Universe identifier (e.g., "CN_Equity_A", "US_Equity").
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
UniverseDetail
|
|
60
|
+
Universe details including the full list of symbols:
|
|
61
|
+
- id: Unique identifier
|
|
62
|
+
- name: Display name
|
|
63
|
+
- region: Region code
|
|
64
|
+
- category: Category
|
|
65
|
+
- symbol_count: Number of symbols
|
|
66
|
+
- symbols: List of symbol codes
|
|
67
|
+
- description: Optional description
|
|
68
|
+
|
|
69
|
+
Examples
|
|
70
|
+
--------
|
|
71
|
+
>>> universe = client.universes.get("CN_Equity_A")
|
|
72
|
+
>>> print(f"Found {len(universe['symbols'])} A-share symbols")
|
|
73
|
+
"""
|
|
74
|
+
response = self._client.get(f"/v1/universes/{universe_id}")
|
|
75
|
+
return response["data"]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AsyncUniverses(AsyncResource):
|
|
79
|
+
"""Asynchronous interface for universe (symbol pool) endpoints.
|
|
80
|
+
|
|
81
|
+
Universes are predefined collections of symbols, such as "A-shares" or "US equities".
|
|
82
|
+
|
|
83
|
+
Examples
|
|
84
|
+
--------
|
|
85
|
+
>>> async with AsyncTickFlow(api_key="your-key") as client:
|
|
86
|
+
... universes = await client.universes.list()
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
async def list(self) -> List["UniverseSummary"]:
|
|
90
|
+
"""Get list of all available universes.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
list of UniverseSummary
|
|
95
|
+
List of universe summaries containing:
|
|
96
|
+
- id: Unique identifier (e.g., "CN_Equity_A")
|
|
97
|
+
- name: Display name
|
|
98
|
+
- region: Region code
|
|
99
|
+
- category: Category (equity, etf, index, etc.)
|
|
100
|
+
- symbol_count: Number of symbols
|
|
101
|
+
- description: Optional description
|
|
102
|
+
|
|
103
|
+
Examples
|
|
104
|
+
--------
|
|
105
|
+
>>> universes = await client.universes.list()
|
|
106
|
+
>>> cn_equity = next(u for u in universes if u['id'] == 'CN_Equity_A')
|
|
107
|
+
>>> print(f"A-shares: {cn_equity['symbol_count']} symbols")
|
|
108
|
+
"""
|
|
109
|
+
response = await self._client.get("/v1/universes")
|
|
110
|
+
return response["data"]
|
|
111
|
+
|
|
112
|
+
async def get(self, universe_id: str) -> "UniverseDetail":
|
|
113
|
+
"""Get detailed information for a specific universe.
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
universe_id : str
|
|
118
|
+
Universe identifier (e.g., "CN_Equity_A", "US_Equity").
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
UniverseDetail
|
|
123
|
+
Universe details including the full list of symbols:
|
|
124
|
+
- id: Unique identifier
|
|
125
|
+
- name: Display name
|
|
126
|
+
- region: Region code
|
|
127
|
+
- category: Category
|
|
128
|
+
- symbol_count: Number of symbols
|
|
129
|
+
- symbols: List of symbol codes
|
|
130
|
+
- description: Optional description
|
|
131
|
+
|
|
132
|
+
Examples
|
|
133
|
+
--------
|
|
134
|
+
>>> universe = await client.universes.get("CN_Equity_A")
|
|
135
|
+
>>> print(f"Found {len(universe['symbols'])} A-share symbols")
|
|
136
|
+
"""
|
|
137
|
+
response = await self._client.get(f"/v1/universes/{universe_id}")
|
|
138
|
+
return response["data"]
|