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/client.py ADDED
@@ -0,0 +1,264 @@
1
+ """Main client classes for TickFlow API.
2
+
3
+ This module provides the primary interfaces for interacting with the TickFlow API:
4
+ - `TickFlow`: Synchronous client
5
+ - `AsyncTickFlow`: Asynchronous client
6
+
7
+ Both clients provide access to the same resources with consistent method signatures.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any, Optional
13
+
14
+ from ._base_client import AsyncAPIClient, SyncAPIClient
15
+ from ._types import Headers, Timeout
16
+ from .resources import (
17
+ AsyncExchanges,
18
+ AsyncKlines,
19
+ AsyncQuotes,
20
+ AsyncSymbols,
21
+ AsyncUniverses,
22
+ Exchanges,
23
+ Klines,
24
+ Quotes,
25
+ Symbols,
26
+ Universes,
27
+ )
28
+
29
+ __all__ = ["TickFlow", "AsyncTickFlow"]
30
+
31
+
32
+ class TickFlow:
33
+ """Synchronous client for TickFlow market data API.
34
+
35
+ Provides access to market data including K-lines, quotes, symbol metadata,
36
+ exchanges, and universes.
37
+
38
+ Parameters
39
+ ----------
40
+ api_key : str, optional
41
+ API key for authentication. If not provided, reads from TICKFLOW_API_KEY
42
+ environment variable.
43
+ base_url : str, optional
44
+ Base URL for the API. Defaults to https://api.tickflow.org.
45
+ Can also be set via TICKFLOW_BASE_URL environment variable.
46
+ timeout : float, optional
47
+ Request timeout in seconds. Defaults to 30.0.
48
+ default_headers : dict, optional
49
+ Default headers to include in all requests.
50
+
51
+ Attributes
52
+ ----------
53
+ klines : Klines
54
+ K-line (OHLCV) data endpoints. Supports DataFrame conversion.
55
+ quotes : Quotes
56
+ Real-time quote endpoints.
57
+ symbols : Symbols
58
+ Symbol metadata endpoints.
59
+ exchanges : Exchanges
60
+ Exchange list endpoints.
61
+ universes : Universes
62
+ Universe (symbol pool) endpoints.
63
+
64
+ Examples
65
+ --------
66
+ Basic usage:
67
+
68
+ >>> from tickflow import TickFlow
69
+ >>>
70
+ >>> # Initialize client
71
+ >>> client = TickFlow(api_key="your-api-key")
72
+ >>>
73
+ >>> # Get K-line data as DataFrame
74
+ >>> df = client.klines.get("600000.SH", period="1d", count=100, as_dataframe=True)
75
+ >>> print(df.tail())
76
+ >>>
77
+ >>> # Get real-time quotes
78
+ >>> quotes = client.quotes.get(symbols=["600000.SH", "AAPL.US"])
79
+ >>> for q in quotes:
80
+ ... print(f"{q['symbol']}: {q['last_price']}")
81
+
82
+ Using context manager:
83
+
84
+ >>> with TickFlow(api_key="your-api-key") as client:
85
+ ... df = client.klines.get("AAPL.US", as_dataframe=True)
86
+ ... print(df.tail())
87
+
88
+ Using environment variable:
89
+
90
+ >>> import os
91
+ >>> os.environ["TICKFLOW_API_KEY"] = "your-api-key"
92
+ >>> client = TickFlow() # Uses TICKFLOW_API_KEY
93
+ """
94
+
95
+ klines: Klines
96
+ quotes: Quotes
97
+ symbols: Symbols
98
+ exchanges: Exchanges
99
+ universes: Universes
100
+
101
+ def __init__(
102
+ self,
103
+ api_key: Optional[str] = None,
104
+ *,
105
+ base_url: Optional[str] = None,
106
+ timeout: Timeout = 30.0,
107
+ default_headers: Optional[Headers] = None,
108
+ ) -> None:
109
+ self._client = SyncAPIClient(
110
+ api_key=api_key,
111
+ base_url=base_url,
112
+ timeout=timeout,
113
+ default_headers=default_headers,
114
+ )
115
+
116
+ # Initialize resources
117
+ self.klines = Klines(self._client)
118
+ self.quotes = Quotes(self._client)
119
+ self.symbols = Symbols(self._client)
120
+ self.exchanges = Exchanges(self._client)
121
+ self.universes = Universes(self._client)
122
+
123
+ def __enter__(self) -> "TickFlow":
124
+ return self
125
+
126
+ def __exit__(self, *args: Any) -> None:
127
+ self.close()
128
+
129
+ def close(self) -> None:
130
+ """Close the underlying HTTP client.
131
+
132
+ This releases any network resources held by the client.
133
+ Called automatically when using the client as a context manager.
134
+ """
135
+ self._client.close()
136
+
137
+ @property
138
+ def api_key(self) -> str:
139
+ """The API key used for authentication."""
140
+ return self._client.api_key
141
+
142
+ @property
143
+ def base_url(self) -> str:
144
+ """The base URL for API requests."""
145
+ return self._client.base_url
146
+
147
+
148
+ class AsyncTickFlow:
149
+ """Asynchronous client for TickFlow market data API.
150
+
151
+ Provides access to market data including K-lines, quotes, symbol metadata,
152
+ exchanges, and universes. All methods are async and must be awaited.
153
+
154
+ Parameters
155
+ ----------
156
+ api_key : str, optional
157
+ API key for authentication. If not provided, reads from TICKFLOW_API_KEY
158
+ environment variable.
159
+ base_url : str, optional
160
+ Base URL for the API. Defaults to https://api.tickflow.org.
161
+ Can also be set via TICKFLOW_BASE_URL environment variable.
162
+ timeout : float, optional
163
+ Request timeout in seconds. Defaults to 30.0.
164
+ default_headers : dict, optional
165
+ Default headers to include in all requests.
166
+
167
+ Attributes
168
+ ----------
169
+ klines : AsyncKlines
170
+ K-line (OHLCV) data endpoints. Supports DataFrame conversion.
171
+ quotes : AsyncQuotes
172
+ Real-time quote endpoints.
173
+ symbols : AsyncSymbols
174
+ Symbol metadata endpoints.
175
+ exchanges : AsyncExchanges
176
+ Exchange list endpoints.
177
+ universes : AsyncUniverses
178
+ Universe (symbol pool) endpoints.
179
+
180
+ Examples
181
+ --------
182
+ Basic usage:
183
+
184
+ >>> import asyncio
185
+ >>> from tickflow import AsyncTickFlow
186
+ >>>
187
+ >>> async def main():
188
+ ... async with AsyncTickFlow(api_key="your-api-key") as client:
189
+ ... # Get K-line data as DataFrame
190
+ ... df = await client.klines.get("600000.SH", as_dataframe=True)
191
+ ... print(df.tail())
192
+ ...
193
+ ... # Get multiple symbols in parallel
194
+ ... import asyncio
195
+ ... tasks = [
196
+ ... client.klines.get(s, as_dataframe=True)
197
+ ... for s in ["600000.SH", "000001.SZ", "AAPL.US"]
198
+ ... ]
199
+ ... results = await asyncio.gather(*tasks)
200
+ >>>
201
+ >>> asyncio.run(main())
202
+
203
+ Manual resource management:
204
+
205
+ >>> async def main():
206
+ ... client = AsyncTickFlow(api_key="your-api-key")
207
+ ... try:
208
+ ... quotes = await client.quotes.get(symbols=["AAPL.US"])
209
+ ... print(quotes)
210
+ ... finally:
211
+ ... await client.close()
212
+ """
213
+
214
+ klines: AsyncKlines
215
+ quotes: AsyncQuotes
216
+ symbols: AsyncSymbols
217
+ exchanges: AsyncExchanges
218
+ universes: AsyncUniverses
219
+
220
+ def __init__(
221
+ self,
222
+ api_key: Optional[str] = None,
223
+ *,
224
+ base_url: Optional[str] = None,
225
+ timeout: Timeout = 30.0,
226
+ default_headers: Optional[Headers] = None,
227
+ ) -> None:
228
+ self._client = AsyncAPIClient(
229
+ api_key=api_key,
230
+ base_url=base_url,
231
+ timeout=timeout,
232
+ default_headers=default_headers,
233
+ )
234
+
235
+ # Initialize resources
236
+ self.klines = AsyncKlines(self._client)
237
+ self.quotes = AsyncQuotes(self._client)
238
+ self.symbols = AsyncSymbols(self._client)
239
+ self.exchanges = AsyncExchanges(self._client)
240
+ self.universes = AsyncUniverses(self._client)
241
+
242
+ async def __aenter__(self) -> "AsyncTickFlow":
243
+ return self
244
+
245
+ async def __aexit__(self, *args: Any) -> None:
246
+ await self.close()
247
+
248
+ async def close(self) -> None:
249
+ """Close the underlying HTTP client.
250
+
251
+ This releases any network resources held by the client.
252
+ Called automatically when using the client as an async context manager.
253
+ """
254
+ await self._client.close()
255
+
256
+ @property
257
+ def api_key(self) -> str:
258
+ """The API key used for authentication."""
259
+ return self._client.api_key
260
+
261
+ @property
262
+ def base_url(self) -> str:
263
+ """The base URL for API requests."""
264
+ return self._client.base_url
@@ -0,0 +1,261 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: openapi.json
3
+ # timestamp: 2026-02-03T10:15:08+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Dict, List, Literal, Optional, TypedDict, Union
8
+
9
+ from typing_extensions import NotRequired, TypeAlias
10
+
11
+
12
+ class ApiError(TypedDict):
13
+ code: str
14
+ details: NotRequired[Any]
15
+ message: str
16
+
17
+
18
+ class BidAsk(TypedDict):
19
+ ask_prices: List[float]
20
+ ask_volumes: List[int]
21
+ bid_prices: List[float]
22
+ bid_volumes: List[int]
23
+
24
+
25
+ class CNQuoteExt(TypedDict):
26
+ bid_ask: NotRequired[Optional[BidAsk]]
27
+ limit_down: float
28
+ limit_up: float
29
+ market_cap: NotRequired[Optional[float]]
30
+
31
+
32
+ class CNSymbolExt(TypedDict):
33
+ delist_date: NotRequired[Optional[int]]
34
+ down_limit: NotRequired[Optional[float]]
35
+ float_shares: NotRequired[Optional[float]]
36
+ is_trading: NotRequired[bool]
37
+ name_en: NotRequired[Optional[str]]
38
+ prev_close: NotRequired[Optional[float]]
39
+ total_shares: NotRequired[Optional[float]]
40
+ up_limit: NotRequired[Optional[float]]
41
+
42
+
43
+ class CompactKlineData(TypedDict):
44
+ amount: NotRequired[List[float]]
45
+ close: List[float]
46
+ high: List[float]
47
+ low: List[float]
48
+ open: List[float]
49
+ timestamp: List[int]
50
+ volume: List[int]
51
+
52
+
53
+ class ExchangeSummary(TypedDict):
54
+ count: int
55
+ exchange: str
56
+ region: str
57
+
58
+
59
+ class ExchangeSymbolsResponse(TypedDict):
60
+ count: int
61
+ data: List[str]
62
+ exchange: str
63
+
64
+
65
+ class HKQuoteExt(TypedDict):
66
+ dividend_yield: NotRequired[Optional[float]]
67
+ lot_size: int
68
+ spread: NotRequired[Optional[float]]
69
+
70
+
71
+ class HKSymbolExt(TypedDict):
72
+ is_trading: NotRequired[bool]
73
+ lot_size: NotRequired[Optional[int]]
74
+ name_cn: NotRequired[Optional[str]]
75
+ prev_close: NotRequired[Optional[float]]
76
+
77
+
78
+ class Kline(TypedDict):
79
+ amount: NotRequired[float]
80
+ close: float
81
+ high: float
82
+ low: float
83
+ open: float
84
+ timestamp: int
85
+ volume: int
86
+
87
+
88
+ class KlinesBatchQuery(TypedDict):
89
+ count: NotRequired[int]
90
+ end: NotRequired[Optional[int]]
91
+ period: str
92
+ start: NotRequired[Optional[int]]
93
+ symbols: str
94
+
95
+
96
+ class KlinesBatchResponse(TypedDict):
97
+ data: Dict[str, CompactKlineData]
98
+
99
+
100
+ class KlinesQuery(TypedDict):
101
+ count: NotRequired[int]
102
+ end: NotRequired[Optional[int]]
103
+ period: NotRequired[str]
104
+ start: NotRequired[Optional[int]]
105
+ symbol: str
106
+
107
+
108
+ class KlinesResponse(TypedDict):
109
+ data: CompactKlineData
110
+
111
+
112
+ Period: TypeAlias = Literal[
113
+ "1m", "5m", "10m", "15m", "30m", "60m", "4h", "1d", "1w", "1M"
114
+ ]
115
+
116
+
117
+ class QuoteExtension1(CNQuoteExt):
118
+ type: Literal["CN"]
119
+
120
+
121
+ class QuoteExtension3(HKQuoteExt):
122
+ type: Literal["HK"]
123
+
124
+
125
+ class QuotesQuery(TypedDict):
126
+ symbols: str
127
+
128
+
129
+ class QuotesRequest(TypedDict):
130
+ symbols: List[str]
131
+
132
+
133
+ Region: TypeAlias = Literal["CN", "US", "HK"]
134
+
135
+
136
+ SessionStatus: TypeAlias = Literal[
137
+ "pre_market", "regular", "after_hours", "closed", "halted", "lunch_break"
138
+ ]
139
+
140
+
141
+ class SymbolMetaExt1(CNSymbolExt):
142
+ type: Literal["CN"]
143
+
144
+
145
+ class SymbolMetaExt3(HKSymbolExt):
146
+ type: Literal["HK"]
147
+
148
+
149
+ class SymbolsMetaRequest(TypedDict):
150
+ symbols: List[str]
151
+
152
+
153
+ class SymbolsQuery(TypedDict):
154
+ symbols: str
155
+
156
+
157
+ class USQuoteExt(TypedDict):
158
+ after_hours_change_pct: NotRequired[Optional[float]]
159
+ after_hours_price: NotRequired[Optional[float]]
160
+ avg_volume_30d: NotRequired[Optional[int]]
161
+ pre_market_change_pct: NotRequired[Optional[float]]
162
+ pre_market_price: NotRequired[Optional[float]]
163
+ week52_high: NotRequired[Optional[float]]
164
+ week52_low: NotRequired[Optional[float]]
165
+
166
+
167
+ class USSymbolExt(TypedDict):
168
+ full_name: NotRequired[Optional[str]]
169
+ market_cap: NotRequired[Optional[float]]
170
+ sector: NotRequired[Optional[str]]
171
+
172
+
173
+ class Universe(TypedDict):
174
+ category: str
175
+ description: NotRequired[Optional[str]]
176
+ id: str
177
+ name: str
178
+ region: str
179
+ symbols: List[str]
180
+
181
+
182
+ class UniverseQuery(TypedDict):
183
+ universes: str
184
+
185
+
186
+ class UniverseSummary(TypedDict):
187
+ category: str
188
+ description: NotRequired[Optional[str]]
189
+ id: str
190
+ name: str
191
+ region: str
192
+ symbol_count: int
193
+
194
+
195
+ class ExchangeListResponse(TypedDict):
196
+ data: List[ExchangeSummary]
197
+
198
+
199
+ class QuoteExtension2(USQuoteExt):
200
+ type: Literal["US"]
201
+
202
+
203
+ QuoteExtension: TypeAlias = Union[QuoteExtension1, QuoteExtension2, QuoteExtension3]
204
+
205
+
206
+ class SymbolMetaExt2(USSymbolExt):
207
+ type: Literal["US"]
208
+
209
+
210
+ SymbolMetaExt: TypeAlias = Union[SymbolMetaExt1, SymbolMetaExt2, SymbolMetaExt3]
211
+
212
+
213
+ class UniverseDetail(UniverseSummary):
214
+ symbols: List[str]
215
+
216
+
217
+ class UniverseDetailResponse(TypedDict):
218
+ data: UniverseDetail
219
+
220
+
221
+ class UniverseListResponse(TypedDict):
222
+ data: List[UniverseSummary]
223
+
224
+
225
+ class Quote(TypedDict):
226
+ amount: float
227
+ ext: NotRequired[Optional[QuoteExtension]]
228
+ high: float
229
+ last_price: float
230
+ low: float
231
+ name: NotRequired[Optional[str]]
232
+ open: float
233
+ prev_close: float
234
+ region: Region
235
+ session: NotRequired[Optional[SessionStatus]]
236
+ symbol: str
237
+ timestamp: int
238
+ volume: int
239
+
240
+
241
+ class QuotesResponse(TypedDict):
242
+ data: Dict[str, Quote]
243
+
244
+
245
+ class SymbolMeta(TypedDict):
246
+ code: str
247
+ exchange: str
248
+ ext: NotRequired[Optional[SymbolMetaExt]]
249
+ list_date: NotRequired[Optional[int]]
250
+ name: NotRequired[Optional[str]]
251
+ region: str
252
+ symbol: str
253
+ symbol_type: NotRequired[Optional[str]]
254
+
255
+
256
+ class SymbolMetaResponse(TypedDict):
257
+ data: List[SymbolMeta]
258
+
259
+
260
+ class UniverseSnapshotResponse(TypedDict):
261
+ data: List[Quote]
@@ -0,0 +1,20 @@
1
+ """Resource modules for TickFlow API."""
2
+
3
+ from .exchanges import AsyncExchanges, Exchanges
4
+ from .klines import AsyncKlines, Klines
5
+ from .quotes import AsyncQuotes, Quotes
6
+ from .symbols import AsyncSymbols, Symbols
7
+ from .universes import AsyncUniverses, Universes
8
+
9
+ __all__ = [
10
+ "Exchanges",
11
+ "AsyncExchanges",
12
+ "Klines",
13
+ "AsyncKlines",
14
+ "Quotes",
15
+ "AsyncQuotes",
16
+ "Symbols",
17
+ "AsyncSymbols",
18
+ "Universes",
19
+ "AsyncUniverses",
20
+ ]
@@ -0,0 +1,29 @@
1
+ """Base resource class for API resources."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Generic, TypeVar
6
+
7
+ if TYPE_CHECKING:
8
+ from .._base_client import AsyncAPIClient, SyncAPIClient
9
+
10
+
11
+ ClientT = TypeVar("ClientT", "SyncAPIClient", "AsyncAPIClient")
12
+
13
+
14
+ class SyncResource:
15
+ """Base class for synchronous API resources."""
16
+
17
+ _client: "SyncAPIClient"
18
+
19
+ def __init__(self, client: "SyncAPIClient") -> None:
20
+ self._client = client
21
+
22
+
23
+ class AsyncResource:
24
+ """Base class for asynchronous API resources."""
25
+
26
+ _client: "AsyncAPIClient"
27
+
28
+ def __init__(self, client: "AsyncAPIClient") -> None:
29
+ self._client = client
@@ -0,0 +1,116 @@
1
+ """Exchange 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 ExchangeSummary
11
+
12
+
13
+ class Exchanges(SyncResource):
14
+ """Synchronous interface for exchange endpoints.
15
+
16
+ Examples
17
+ --------
18
+ >>> client = TickFlow(api_key="your-key")
19
+ >>> exchanges = client.exchanges.list()
20
+ >>> for exchange in exchanges:
21
+ ... print(f"{exchange['exchange']}: {exchange['count']} symbols")
22
+ """
23
+
24
+ def list(self) -> List["ExchangeSummary"]:
25
+ """Get list of all exchanges with symbol counts.
26
+
27
+ Returns
28
+ -------
29
+ list of ExchangeSummary
30
+ List of exchange information containing:
31
+ - exchange: Exchange code (e.g., "SH", "SZ", "US")
32
+ - region: Region code (e.g., "CN", "US", "HK")
33
+ - count: Number of symbols in the exchange
34
+
35
+ Examples
36
+ --------
37
+ >>> exchanges = client.exchanges.list()
38
+ >>> print(exchanges)
39
+ [{'exchange': 'SH', 'region': 'CN', 'count': 2100}, ...]
40
+ """
41
+ response = self._client.get("/v1/exchanges")
42
+ return response["data"]
43
+
44
+ def get_symbols(self, exchange: str) -> List[str]:
45
+ """Get all symbols for a specific exchange.
46
+
47
+ Parameters
48
+ ----------
49
+ exchange : str
50
+ Exchange code (e.g., "SH", "SZ", "BJ", "US", "HK").
51
+
52
+ Returns
53
+ -------
54
+ list of str
55
+ List of symbol codes.
56
+
57
+ Examples
58
+ --------
59
+ >>> symbols = client.exchanges.get_symbols("SH")
60
+ >>> print(symbols[:5])
61
+ ['600000.SH', '600001.SH', '600002.SH', ...]
62
+ """
63
+ response = self._client.get(f"/v1/exchanges/{exchange}/symbols")
64
+ return response["data"]
65
+
66
+
67
+ class AsyncExchanges(AsyncResource):
68
+ """Asynchronous interface for exchange endpoints.
69
+
70
+ Examples
71
+ --------
72
+ >>> async with AsyncTickFlow(api_key="your-key") as client:
73
+ ... exchanges = await client.exchanges.list()
74
+ """
75
+
76
+ async def list(self) -> List["ExchangeSummary"]:
77
+ """Get list of all exchanges with symbol counts.
78
+
79
+ Returns
80
+ -------
81
+ list of ExchangeSummary
82
+ List of exchange information containing:
83
+ - exchange: Exchange code (e.g., "SH", "SZ", "US")
84
+ - region: Region code (e.g., "CN", "US", "HK")
85
+ - count: Number of symbols in the exchange
86
+
87
+ Examples
88
+ --------
89
+ >>> exchanges = await client.exchanges.list()
90
+ >>> print(exchanges)
91
+ [{'exchange': 'SH', 'region': 'CN', 'count': 2100}, ...]
92
+ """
93
+ response = await self._client.get("/v1/exchanges")
94
+ return response["data"]
95
+
96
+ async def get_symbols(self, exchange: str) -> List[str]:
97
+ """Get all symbols for a specific exchange.
98
+
99
+ Parameters
100
+ ----------
101
+ exchange : str
102
+ Exchange code (e.g., "SH", "SZ", "BJ", "US", "HK").
103
+
104
+ Returns
105
+ -------
106
+ list of str
107
+ List of symbol codes.
108
+
109
+ Examples
110
+ --------
111
+ >>> symbols = await client.exchanges.get_symbols("SH")
112
+ >>> print(symbols[:5])
113
+ ['600000.SH', '600001.SH', '600002.SH', ...]
114
+ """
115
+ response = await self._client.get(f"/v1/exchanges/{exchange}/symbols")
116
+ return response["data"]