alphafeed 0.1.0.dev0__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.
@@ -0,0 +1,724 @@
1
+ """K-line (OHLCV) data resources for AlphaFeed API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import concurrent.futures
6
+ from typing import (
7
+ TYPE_CHECKING,
8
+ Any,
9
+ Dict,
10
+ List,
11
+ Literal,
12
+ Optional,
13
+ Tuple,
14
+ Union,
15
+ overload,
16
+ )
17
+
18
+ import pandas as pd
19
+
20
+ from .._batch import _chunk_list, _get_progress_bar, batched_get_sync
21
+ from .._cache import InstrumentNameCache
22
+ from .._types import NOT_GIVEN, NotGiven
23
+ from ._base import SyncResource
24
+
25
+ if TYPE_CHECKING:
26
+ from ..models import AdjustType, CompactKlineData, ExFactorEntry, Period
27
+
28
+ MAX_SYMBOLS_PER_BATCH = 100
29
+
30
+
31
+ def _klines_to_dataframe(
32
+ data: "CompactKlineData",
33
+ symbol: Optional[str] = None,
34
+ name: Optional[str] = None,
35
+ ) -> "pd.DataFrame":
36
+ """Convert compact K-line data to a pandas DataFrame.
37
+
38
+ Parameters
39
+ ----------
40
+ data : CompactKlineData
41
+ Compact columnar K-line data from the API.
42
+ symbol : str, optional
43
+ Symbol code to include as a column.
44
+ name : str, optional
45
+ Instrument name to include as a column.
46
+
47
+ Returns
48
+ -------
49
+ pd.DataFrame
50
+ DataFrame with columns: timestamp, open, high, low, close, volume, amount.
51
+ """
52
+ import datetime
53
+
54
+ from ..utils import get_instrument_region, get_region_timezone
55
+
56
+ timestamps = data["timestamp"]
57
+
58
+ if not timestamps:
59
+ trade_dates = []
60
+ trade_times = []
61
+ else:
62
+ region = get_instrument_region(symbol) if symbol else None
63
+ tz = get_region_timezone(region) if region else None
64
+
65
+ if tz:
66
+ trade_dates = []
67
+ trade_times = []
68
+ append_date = trade_dates.append
69
+ append_time = trade_times.append
70
+ fromtimestamp = datetime.datetime.fromtimestamp
71
+
72
+ for ts in timestamps:
73
+ dt = fromtimestamp(ts / 1000, tz)
74
+ append_date(f"{dt.year:04d}-{dt.month:02d}-{dt.day:02d}")
75
+ append_time(
76
+ f"{dt.year:04d}-{dt.month:02d}-{dt.day:02d} {dt.hour:02d}:{dt.minute:02d}:{dt.second:02d}"
77
+ )
78
+ else:
79
+ n = len(timestamps)
80
+ trade_dates = [None] * n
81
+ trade_times = [None] * n
82
+
83
+ df = pd.DataFrame(
84
+ {
85
+ "symbol": symbol,
86
+ "name": name,
87
+ "timestamp": timestamps,
88
+ "trade_date": trade_dates,
89
+ "trade_time": trade_times,
90
+ "open": data["open"],
91
+ "high": data["high"],
92
+ "low": data["low"],
93
+ "close": data["close"],
94
+ "volume": data["volume"],
95
+ "amount": (data["amount"] if "amount" in data else [0.0] * len(timestamps)),
96
+ }
97
+ )
98
+
99
+ return df
100
+
101
+
102
+ def _batch_klines_to_dataframes(
103
+ data: Dict[str, "CompactKlineData"],
104
+ names: Optional[Dict[str, str]] = None,
105
+ ) -> Dict[str, "pd.DataFrame"]:
106
+ """Convert batch K-line data to DataFrames with optional name column.
107
+
108
+ Parameters
109
+ ----------
110
+ data : dict
111
+ Dictionary mapping symbol codes to compact K-line data.
112
+ names : dict, optional
113
+ Dictionary mapping symbol codes to instrument names.
114
+
115
+ Returns
116
+ -------
117
+ dict of str to pd.DataFrame
118
+ Dictionary mapping symbol codes to pandas DataFrames.
119
+ """
120
+ names = names or {}
121
+ dfs: Dict[str, "pd.DataFrame"] = {}
122
+ for symbol, kline_data in data.items():
123
+ df = _klines_to_dataframe(kline_data, symbol=symbol, name=names.get(symbol))
124
+ dfs[symbol] = df
125
+
126
+ return dfs
127
+
128
+
129
+ def _factors_to_dataframe(
130
+ data: Dict[str, List["ExFactorEntry"]],
131
+ ) -> "pd.DataFrame":
132
+ """Convert factor response to a single long-format DataFrame."""
133
+ from ..utils import get_instrument_region, get_region_timezone
134
+
135
+ if not data:
136
+ return pd.DataFrame(columns=["symbol", "timestamp", "trade_date", "ex_factor"])
137
+
138
+ rows = []
139
+ for symbol, entries in data.items():
140
+ if not entries:
141
+ continue
142
+
143
+ region = get_instrument_region(symbol)
144
+ tz = get_region_timezone(region) if region else None
145
+
146
+ timestamps = [e["timestamp"] for e in entries]
147
+
148
+ if tz:
149
+ ts_series = pd.to_datetime(timestamps, unit="ms", utc=True).tz_convert(tz)
150
+ trade_dates = ts_series.strftime("%Y-%m-%d").tolist()
151
+ else:
152
+ trade_dates = [None] * len(timestamps)
153
+
154
+ for i, e in enumerate(entries):
155
+ rows.append(
156
+ {
157
+ "symbol": symbol,
158
+ "timestamp": e["timestamp"],
159
+ "trade_date": trade_dates[i],
160
+ "ex_factor": e["ex_factor"],
161
+ }
162
+ )
163
+
164
+ return (
165
+ pd.DataFrame(rows)
166
+ if rows
167
+ else pd.DataFrame(columns=["symbol", "timestamp", "trade_date", "ex_factor"])
168
+ )
169
+
170
+
171
+ class Klines(SyncResource):
172
+ """Synchronous interface for K-line (OHLCV) data endpoints.
173
+
174
+ Supports returning data as raw dicts or pandas DataFrames.
175
+ When returning DataFrames, instrument names are automatically resolved
176
+ from a local cache (fetched from the instruments API on first access).
177
+
178
+ Examples
179
+ --------
180
+ >>> client = AlphaFeed(api_key="your-key")
181
+ >>>
182
+ >>> # Get raw K-line data
183
+ >>> klines = client.klines.get("600000.SH", count=100)
184
+ >>>
185
+ >>> # Get as DataFrame (includes name column)
186
+ >>> df = client.klines.get("600000.SH", count=100, to_dataframe=True)
187
+ >>> print(df.head())
188
+ """
189
+
190
+ _instrument_cache: Optional[InstrumentNameCache]
191
+
192
+ def __init__(
193
+ self, client: Any, instrument_cache: Optional[InstrumentNameCache] = None
194
+ ) -> None:
195
+ super().__init__(client)
196
+ self._instrument_cache = instrument_cache
197
+
198
+ def _resolve_name(self, symbol: str) -> Optional[str]:
199
+ if not self._instrument_cache:
200
+ return None
201
+ names = self._instrument_cache.resolve_sync([symbol], self._client)
202
+ return names.get(symbol)
203
+
204
+ def _resolve_names(self, symbols: List[str]) -> Dict[str, str]:
205
+ if not self._instrument_cache:
206
+ return {}
207
+ return self._instrument_cache.resolve_sync(symbols, self._client)
208
+
209
+ @overload
210
+ def get(
211
+ self,
212
+ symbol: str,
213
+ *,
214
+ period: Union["Period", NotGiven] = NOT_GIVEN,
215
+ count: Union[int, NotGiven] = NOT_GIVEN,
216
+ start_time: Union[int, None, NotGiven] = NOT_GIVEN,
217
+ end_time: Union[int, None, NotGiven] = NOT_GIVEN,
218
+ adjust: Union["AdjustType", NotGiven] = NOT_GIVEN,
219
+ to_dataframe: Literal[False] = False,
220
+ ) -> "CompactKlineData": ...
221
+
222
+ @overload
223
+ def get(
224
+ self,
225
+ symbol: str,
226
+ *,
227
+ period: Union["Period", NotGiven] = NOT_GIVEN,
228
+ count: Union[int, NotGiven] = NOT_GIVEN,
229
+ start_time: Union[int, None, NotGiven] = NOT_GIVEN,
230
+ end_time: Union[int, None, NotGiven] = NOT_GIVEN,
231
+ adjust: Union["AdjustType", NotGiven] = NOT_GIVEN,
232
+ to_dataframe: Literal[True],
233
+ ) -> "pd.DataFrame": ...
234
+
235
+ def get(
236
+ self,
237
+ symbol: str,
238
+ *,
239
+ period: Union["Period", NotGiven] = NOT_GIVEN,
240
+ count: Union[int, NotGiven] = NOT_GIVEN,
241
+ start_time: Union[int, None, NotGiven] = NOT_GIVEN,
242
+ end_time: Union[int, None, NotGiven] = NOT_GIVEN,
243
+ adjust: Union["AdjustType", NotGiven] = NOT_GIVEN,
244
+ to_dataframe: bool = False,
245
+ ) -> Union["CompactKlineData", "pd.DataFrame"]:
246
+ """Get K-line (OHLCV) data for a single symbol.
247
+
248
+ Parameters
249
+ ----------
250
+ symbol : str
251
+ Symbol code (e.g., "600000.SH", "AAPL.US").
252
+ period : str, optional
253
+ K-line period. One of: "1m", "5m", "10m", "15m", "30m", "60m", "1d", "1w", "1M". Defaults to "1d".
254
+ count : int, optional
255
+ Number of K-lines to return. Default 100, max 10000.
256
+ start_time : int, optional
257
+ Start timestamp in milliseconds.
258
+ end_time : int, optional
259
+ End timestamp in milliseconds.
260
+ adjust : str, optional
261
+ Adjustment type: "forward" (前复权, default), "backward" (后复权),
262
+ or "none" (不复权).
263
+ to_dataframe : bool, optional
264
+ If True, return a pandas DataFrame. If False (default), return
265
+ the raw compact columnar data.
266
+
267
+ Returns
268
+ -------
269
+ CompactKlineData or pd.DataFrame
270
+ If to_dataframe=False: dict with keys 'timestamp', 'open', 'high',
271
+ 'low', 'close', 'volume', 'amount' (each a list).
272
+ If to_dataframe=True: pandas DataFrame with datetime index.
273
+
274
+ Examples
275
+ --------
276
+ >>> # Get forward-adjusted data (default)
277
+ >>> df = client.klines.get("600519.SH", period="1d", count=100, to_dataframe=True)
278
+ >>>
279
+ >>> # Get unadjusted data
280
+ >>> df_raw = client.klines.get("600519.SH", adjust="none", to_dataframe=True)
281
+ >>>
282
+ >>> # Get backward-adjusted data
283
+ >>> df_hfq = client.klines.get("600519.SH", adjust="backward", to_dataframe=True)
284
+ """
285
+ params: Dict[str, Any] = {"symbol": symbol}
286
+ if not isinstance(period, NotGiven):
287
+ params["period"] = period
288
+ if not isinstance(count, NotGiven):
289
+ params["count"] = count
290
+ if not isinstance(start_time, NotGiven) and start_time is not None:
291
+ params["start_time"] = start_time
292
+ if not isinstance(end_time, NotGiven) and end_time is not None:
293
+ params["end_time"] = end_time
294
+ if not isinstance(adjust, NotGiven):
295
+ params["adjust"] = adjust
296
+
297
+ response = self._client.get("/v1/klines", params=params)
298
+ data = response["data"]
299
+
300
+ if to_dataframe:
301
+ name = self._resolve_name(symbol)
302
+ return _klines_to_dataframe(data, symbol=symbol, name=name)
303
+ return data
304
+
305
+ def _fetch_batch_chunk(
306
+ self,
307
+ symbols: List[str],
308
+ params: Dict[str, Any],
309
+ endpoint: str = "/v1/klines/batch",
310
+ ) -> Tuple[Dict[str, "CompactKlineData"], List[Tuple[str, Exception]]]:
311
+ """Fetch a single batch chunk.
312
+
313
+ Returns
314
+ -------
315
+ tuple
316
+ (data dict, list of (symbol, error) for failed symbols)
317
+ """
318
+ symbols_str = ",".join(symbols)
319
+ chunk_params = {**params, "symbols": symbols_str}
320
+
321
+ response = self._client.get(endpoint, params=chunk_params)
322
+ return response["data"], []
323
+
324
+ def _run_batch(
325
+ self,
326
+ symbols: List[str],
327
+ params: Dict[str, Any],
328
+ endpoint: str,
329
+ batch_size: int,
330
+ to_dataframe: bool,
331
+ show_progress: bool,
332
+ max_workers: int,
333
+ progress_desc: str = "Fetching K-lines",
334
+ ) -> Union[Dict[str, "CompactKlineData"], "pd.DataFrame"]:
335
+ if not symbols:
336
+ return {} if not to_dataframe else _batch_klines_to_dataframes({})
337
+
338
+ chunks = _chunk_list(symbols, batch_size)
339
+
340
+ if len(chunks) == 1:
341
+ data, errors = self._fetch_batch_chunk(chunks[0], params, endpoint)
342
+ if to_dataframe:
343
+ names = self._resolve_names(list(data.keys()))
344
+ return _batch_klines_to_dataframes(data, names=names)
345
+ return data
346
+
347
+ pbar = _get_progress_bar(len(chunks), progress_desc, show_progress)
348
+
349
+ all_data: Dict[str, "CompactKlineData"] = {}
350
+ all_errors: List[Tuple[str, Exception]] = []
351
+
352
+ try:
353
+ with concurrent.futures.ThreadPoolExecutor(
354
+ max_workers=max_workers
355
+ ) as executor:
356
+ futures = {
357
+ executor.submit(
358
+ self._fetch_batch_chunk, chunk, params, endpoint
359
+ ): chunk
360
+ for chunk in chunks
361
+ }
362
+
363
+ for future in concurrent.futures.as_completed(futures):
364
+ try:
365
+ data, errors = future.result()
366
+ all_data.update(data)
367
+ all_errors.extend(errors)
368
+ except Exception as e:
369
+ chunk = futures[future]
370
+ all_errors.extend((s, e) for s in chunk)
371
+
372
+ if pbar:
373
+ pbar.update(1)
374
+ finally:
375
+ if pbar:
376
+ pbar.close()
377
+
378
+ if to_dataframe:
379
+ names = self._resolve_names(list(all_data.keys()))
380
+ return _batch_klines_to_dataframes(all_data, names=names)
381
+ return all_data
382
+
383
+ @overload
384
+ def batch(
385
+ self,
386
+ symbols: List[str],
387
+ *,
388
+ period: Union["Period", NotGiven] = NOT_GIVEN,
389
+ count: Union[int, NotGiven] = NOT_GIVEN,
390
+ start_time: Union[int, None, NotGiven] = NOT_GIVEN,
391
+ end_time: Union[int, None, NotGiven] = NOT_GIVEN,
392
+ adjust: Union["AdjustType", NotGiven] = NOT_GIVEN,
393
+ to_dataframe: Literal[False] = False,
394
+ show_progress: bool = False,
395
+ max_workers: int = 5,
396
+ batch_size: int = MAX_SYMBOLS_PER_BATCH,
397
+ ) -> Dict[str, "CompactKlineData"]: ...
398
+
399
+ @overload
400
+ def batch(
401
+ self,
402
+ symbols: List[str],
403
+ *,
404
+ period: Union["Period", NotGiven] = NOT_GIVEN,
405
+ count: Union[int, NotGiven] = NOT_GIVEN,
406
+ start_time: Union[int, None, NotGiven] = NOT_GIVEN,
407
+ end_time: Union[int, None, NotGiven] = NOT_GIVEN,
408
+ adjust: Union["AdjustType", NotGiven] = NOT_GIVEN,
409
+ to_dataframe: Literal[True],
410
+ show_progress: bool = False,
411
+ max_workers: int = 5,
412
+ batch_size: int = MAX_SYMBOLS_PER_BATCH,
413
+ ) -> "pd.DataFrame": ...
414
+
415
+ def batch(
416
+ self,
417
+ symbols: List[str],
418
+ *,
419
+ period: Union["Period", NotGiven] = NOT_GIVEN,
420
+ count: Union[int, NotGiven] = NOT_GIVEN,
421
+ start_time: Union[int, None, NotGiven] = NOT_GIVEN,
422
+ end_time: Union[int, None, NotGiven] = NOT_GIVEN,
423
+ adjust: Union["AdjustType", NotGiven] = NOT_GIVEN,
424
+ to_dataframe: bool = False,
425
+ show_progress: bool = False,
426
+ max_workers: int = 5,
427
+ batch_size: int = MAX_SYMBOLS_PER_BATCH,
428
+ ) -> Union[Dict[str, "CompactKlineData"], "pd.DataFrame"]:
429
+ """Get K-line data for multiple symbols in batched concurrent requests.
430
+
431
+ Automatically splits large symbol lists into chunks and fetches them
432
+ concurrently. Failed chunks don't affect other chunks.
433
+
434
+ Parameters
435
+ ----------
436
+ symbols : list of str
437
+ List of symbol codes.
438
+ period : str, optional
439
+ K-line period. Defaults to "1d".
440
+ count : int, optional
441
+ Number of K-lines per symbol. Default 100.
442
+ start_time : int, optional
443
+ Start timestamp in milliseconds.
444
+ end_time : int, optional
445
+ End timestamp in milliseconds.
446
+ adjust : str, optional
447
+ Adjustment type: "forward" (前复权, default), "backward" (后复权),
448
+ or "none" (不复权).
449
+ to_dataframe : bool, optional
450
+ If True, return dict of DataFrames. Default False.
451
+ show_progress : bool, optional
452
+ If True, display a progress bar (requires tqdm). Default False.
453
+ max_workers : int, optional
454
+ Maximum number of concurrent requests. Default 5.
455
+ batch_size : int, optional
456
+ Number of symbols per request. Default 100.
457
+ Reduce this if the server enforces a lower per-request limit.
458
+
459
+ Returns
460
+ -------
461
+ dict or pd.DataFrame
462
+ If to_dataframe=False: dict mapping symbol codes to CompactKlineData.
463
+ If to_dataframe=True: dict mapping symbol codes to DataFrames.
464
+
465
+ Examples
466
+ --------
467
+ >>> symbols = ["600000.SH", "000001.SZ", "600519.SH"]
468
+ >>> dfs = client.klines.batch(symbols, to_dataframe=True, show_progress=True)
469
+ """
470
+ params: Dict[str, Any] = {}
471
+ if not isinstance(period, NotGiven):
472
+ params["period"] = period
473
+ if not isinstance(count, NotGiven):
474
+ params["count"] = count
475
+ if not isinstance(start_time, NotGiven) and start_time is not None:
476
+ params["start_time"] = start_time
477
+ if not isinstance(end_time, NotGiven) and end_time is not None:
478
+ params["end_time"] = end_time
479
+ if not isinstance(adjust, NotGiven):
480
+ params["adjust"] = adjust
481
+
482
+ return self._run_batch(
483
+ symbols=symbols,
484
+ params=params,
485
+ endpoint="/v1/klines/batch",
486
+ batch_size=batch_size,
487
+ to_dataframe=to_dataframe,
488
+ show_progress=show_progress,
489
+ max_workers=max_workers,
490
+ progress_desc="Fetching K-lines",
491
+ )
492
+
493
+ @overload
494
+ def intraday(
495
+ self,
496
+ symbol: str,
497
+ *,
498
+ period: Union["Period", NotGiven] = NOT_GIVEN,
499
+ count: Union[int, NotGiven] = NOT_GIVEN,
500
+ to_dataframe: Literal[False] = False,
501
+ ) -> "CompactKlineData": ...
502
+
503
+ @overload
504
+ def intraday(
505
+ self,
506
+ symbol: str,
507
+ *,
508
+ period: Union["Period", NotGiven] = NOT_GIVEN,
509
+ count: Union[int, NotGiven] = NOT_GIVEN,
510
+ to_dataframe: Literal[True],
511
+ ) -> "pd.DataFrame": ...
512
+
513
+ def intraday(
514
+ self,
515
+ symbol: str,
516
+ *,
517
+ period: Union["Period", NotGiven] = NOT_GIVEN,
518
+ count: Union[int, NotGiven] = NOT_GIVEN,
519
+ to_dataframe: bool = False,
520
+ ) -> Union["CompactKlineData", "pd.DataFrame"]:
521
+ """Get intraday minute K-line data for a single symbol.
522
+
523
+ This endpoint returns today's minute-level K-line data. The data is
524
+ continuously updated during trading hours.
525
+
526
+ Parameters
527
+ ----------
528
+ symbol : str
529
+ Symbol code (e.g., "600000.SH", "AAPL.US").
530
+ period : str, optional
531
+ K-line period. One of: "1m", "5m", "15m", "30m", "60m".
532
+ Defaults to "1m".
533
+ count : int, optional
534
+ Number of K-lines to return. If not specified, returns all
535
+ available data for today.
536
+ to_dataframe : bool, optional
537
+ If True, return a pandas DataFrame. If False (default), return
538
+ the raw compact columnar data.
539
+
540
+ Returns
541
+ -------
542
+ CompactKlineData or pd.DataFrame
543
+ If to_dataframe=False: dict with keys 'timestamp', 'open', 'high',
544
+ 'low', 'close', 'volume', 'amount' (each a list).
545
+ If to_dataframe=True: pandas DataFrame with datetime index.
546
+
547
+ Examples
548
+ --------
549
+ >>> # Get today's 1-minute K-lines
550
+ >>> data = client.klines.intraday("600000.SH")
551
+ >>> print(f"Got {len(data['timestamp'])} bars")
552
+
553
+ >>> # Get as DataFrame
554
+ >>> df = client.klines.intraday("600000.SH", to_dataframe=True)
555
+ >>> print(df.tail())
556
+
557
+ >>> # Get 5-minute aggregated data
558
+ >>> df = client.klines.intraday("600000.SH", period="5m", to_dataframe=True)
559
+ """
560
+ params: Dict[str, Any] = {"symbol": symbol}
561
+ if not isinstance(period, NotGiven):
562
+ params["period"] = period
563
+ if not isinstance(count, NotGiven):
564
+ params["count"] = count
565
+
566
+ response = self._client.get("/v1/klines/intraday", params=params)
567
+ data = response["data"]
568
+
569
+ if to_dataframe:
570
+ name = self._resolve_name(symbol)
571
+ return _klines_to_dataframe(data, symbol=symbol, name=name)
572
+ return data
573
+
574
+ @overload
575
+ def intraday_batch(
576
+ self,
577
+ symbols: List[str],
578
+ *,
579
+ period: Union["Period", NotGiven] = NOT_GIVEN,
580
+ count: Union[int, NotGiven] = NOT_GIVEN,
581
+ to_dataframe: Literal[False] = False,
582
+ show_progress: bool = False,
583
+ max_workers: int = 5,
584
+ batch_size: int = MAX_SYMBOLS_PER_BATCH,
585
+ ) -> Dict[str, "CompactKlineData"]: ...
586
+
587
+ @overload
588
+ def intraday_batch(
589
+ self,
590
+ symbols: List[str],
591
+ *,
592
+ period: Union["Period", NotGiven] = NOT_GIVEN,
593
+ count: Union[int, NotGiven] = NOT_GIVEN,
594
+ to_dataframe: Literal[True],
595
+ show_progress: bool = False,
596
+ max_workers: int = 5,
597
+ batch_size: int = MAX_SYMBOLS_PER_BATCH,
598
+ ) -> "pd.DataFrame": ...
599
+
600
+ def intraday_batch(
601
+ self,
602
+ symbols: List[str],
603
+ *,
604
+ period: Union["Period", NotGiven] = NOT_GIVEN,
605
+ count: Union[int, NotGiven] = NOT_GIVEN,
606
+ to_dataframe: bool = False,
607
+ show_progress: bool = False,
608
+ max_workers: int = 5,
609
+ batch_size: int = MAX_SYMBOLS_PER_BATCH,
610
+ ) -> Union[Dict[str, "CompactKlineData"], "pd.DataFrame"]:
611
+ """Get intraday K-line data for multiple symbols in batched concurrent requests.
612
+
613
+ Automatically splits large symbol lists into chunks and fetches them
614
+ concurrently. Failed chunks don't affect other chunks.
615
+
616
+ Parameters
617
+ ----------
618
+ symbols : list of str
619
+ List of symbol codes.
620
+ period : str, optional
621
+ K-line period. One of: "1m", "5m", "15m", "30m", "60m".
622
+ Defaults to "1m".
623
+ count : int, optional
624
+ Number of K-lines per symbol. If not specified, returns all
625
+ available data for today.
626
+ to_dataframe : bool, optional
627
+ If True, return dict of DataFrames. Default False.
628
+ show_progress : bool, optional
629
+ If True, display a progress bar (requires tqdm). Default False.
630
+ max_workers : int, optional
631
+ Maximum number of concurrent requests. Default 5.
632
+ batch_size : int, optional
633
+ Number of symbols per request. Default 100.
634
+ Reduce this if the server enforces a lower per-request limit.
635
+
636
+ Returns
637
+ -------
638
+ dict or pd.DataFrame
639
+ If to_dataframe=False: dict mapping symbol codes to CompactKlineData.
640
+ If to_dataframe=True: dict mapping symbol codes to DataFrames.
641
+
642
+ Examples
643
+ --------
644
+ >>> symbols = ["600000.SH", "000001.SZ", "600519.SH"]
645
+ >>> dfs = client.klines.intraday_batch(symbols, to_dataframe=True)
646
+ """
647
+ params: Dict[str, Any] = {}
648
+ if not isinstance(period, NotGiven):
649
+ params["period"] = period
650
+ if not isinstance(count, NotGiven):
651
+ params["count"] = count
652
+
653
+ return self._run_batch(
654
+ symbols=symbols,
655
+ params=params,
656
+ endpoint="/v1/klines/intraday/batch",
657
+ batch_size=batch_size,
658
+ to_dataframe=to_dataframe,
659
+ show_progress=show_progress,
660
+ max_workers=max_workers,
661
+ progress_desc="Fetching intraday K-lines",
662
+ )
663
+
664
+ def ex_factors(
665
+ self,
666
+ symbols: List[str],
667
+ *,
668
+ start_time: Union[int, None, NotGiven] = NOT_GIVEN,
669
+ end_time: Union[int, None, NotGiven] = NOT_GIVEN,
670
+ to_dataframe: bool = False,
671
+ batch_size: int = MAX_SYMBOLS_PER_BATCH,
672
+ show_progress: bool = False,
673
+ max_workers: int = 5,
674
+ ) -> Union[Dict[str, List["ExFactorEntry"]], "pd.DataFrame"]:
675
+ """Get dividend/split adjustment factors for one or more symbols.
676
+
677
+ Parameters
678
+ ----------
679
+ symbols : list of str
680
+ Symbol codes (e.g., ["600519.SH", "000001.SZ"]).
681
+ start_time : int, optional
682
+ Filter: only factors with timestamp >= start_time (ms).
683
+ end_time : int, optional
684
+ Filter: only factors with timestamp <= end_time (ms).
685
+ to_dataframe : bool, optional
686
+ If True, return a long-format pandas DataFrame with columns:
687
+ symbol, timestamp, ex_factor, trade_date.
688
+ batch_size : int, optional
689
+ Max symbols per request (default 100).
690
+ show_progress : bool, optional
691
+ Show a tqdm progress bar for multi-chunk fetches.
692
+ max_workers : int, optional
693
+ Concurrent request threads (default 5).
694
+
695
+ Returns
696
+ -------
697
+ dict or pd.DataFrame
698
+ If to_dataframe=False: ``{symbol: [{timestamp, ex_factor}, ...]}``.
699
+ If to_dataframe=True: long-format DataFrame.
700
+
701
+ Examples
702
+ --------
703
+ >>> factors = client.klines.ex_factors(["600519.SH"])
704
+ >>> df = client.klines.ex_factors(["600519.SH", "000001.SZ"], to_dataframe=True)
705
+ """
706
+ params: Dict[str, Any] = {}
707
+ if not isinstance(start_time, NotGiven) and start_time is not None:
708
+ params["start_time"] = start_time
709
+ if not isinstance(end_time, NotGiven) and end_time is not None:
710
+ params["end_time"] = end_time
711
+
712
+ data = batched_get_sync(
713
+ self._client,
714
+ "/v1/klines/ex-factors",
715
+ symbols,
716
+ params,
717
+ batch_size=batch_size,
718
+ max_workers=max_workers,
719
+ show_progress=show_progress,
720
+ progress_desc="Fetching ex-factors",
721
+ )
722
+ if to_dataframe:
723
+ return _factors_to_dataframe(data)
724
+ return data