qka 1.2.3.dev1__tar.gz → 1.2.4.dev3__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 (45) hide show
  1. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/PKG-INFO +1 -1
  2. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/core/data.py +80 -19
  3. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/.github/workflows/docs.yml +0 -0
  4. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/.github/workflows/release.yml +0 -0
  5. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/.gitignore +0 -0
  6. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/.vscode/settings.json +0 -0
  7. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/CHANGELOG.md +0 -0
  8. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/LICENSE +0 -0
  9. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/README.md +0 -0
  10. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/api/brokers.md +0 -0
  11. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/api/core.md +0 -0
  12. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/api/utils.md +0 -0
  13. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/getting-started/concepts.md +0 -0
  14. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/getting-started/first-strategy.md +0 -0
  15. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/getting-started/installation.md +0 -0
  16. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/index.md +0 -0
  17. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/user-guide/backtest.md +0 -0
  18. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/user-guide/data.md +0 -0
  19. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/docs/user-guide/trading.md +0 -0
  20. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/mkdocs.yml +0 -0
  21. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/pyproject.toml +0 -0
  22. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/__init__.py +0 -0
  23. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/brokers/__init__.py +0 -0
  24. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/brokers/client.py +0 -0
  25. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/brokers/server.py +0 -0
  26. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/brokers/trade.py +0 -0
  27. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/cli.py +0 -0
  28. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/core/__init__.py +0 -0
  29. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/core/backtest.py +0 -0
  30. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/core/broker.py +0 -0
  31. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/core/report.py +0 -0
  32. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/core/strategy.py +0 -0
  33. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/mcp/__init__.py +0 -0
  34. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/mcp/api.py +0 -0
  35. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/mcp/server.py +0 -0
  36. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/server/__init__.py +0 -0
  37. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/server/handlers/__init__.py +0 -0
  38. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/server/handlers/class_inspector_handler.py +0 -0
  39. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/server/handlers/code_executor_handler.py +0 -0
  40. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/server/ws_client.py +0 -0
  41. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/server/zmq_server.py +0 -0
  42. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/utils/__init__.py +0 -0
  43. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/utils/anis.py +0 -0
  44. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/utils/logger.py +0 -0
  45. {qka-1.2.3.dev1 → qka-1.2.4.dev3}/qka/utils/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qka
3
- Version: 1.2.3.dev1
3
+ Version: 1.2.4.dev3
4
4
  Summary: 快捷量化助手(Quick Quantitative Assistant)- 简洁易用的A股量化交易框架
5
5
  Project-URL: Home, https://github.com/zsrl/qka
6
6
  Project-URL: Documentation, https://zsrl.github.io/qka
@@ -11,6 +11,7 @@ import pandas as pd
11
11
  import pyarrow as pa
12
12
  import pyarrow.parquet as pq
13
13
  import akshare as ak
14
+ import baostock as bs
14
15
  import dask.dataframe as dd
15
16
  from typing import List, Dict, Optional, Callable
16
17
  from qka.utils.logger import logger
@@ -26,7 +27,7 @@ class Data():
26
27
  period (str): 数据周期,如 '1d'、'1m' 等
27
28
  adjust (str): 复权方式,如 'qfq'、'hfq'、'bfq'
28
29
  factor (Callable): 因子计算函数
29
- source (str): 数据源,如 'akshare'、'qmt'
30
+ source (str): 数据源,如 'baostock'(默认)、'akshare'、'qmt'
30
31
  pool_size (int): 并发下载线程数
31
32
  datadir (Path): 数据缓存目录
32
33
  target_dir (Path): 目标存储目录
@@ -38,7 +39,7 @@ class Data():
38
39
  period: str = '1d',
39
40
  adjust: str = 'qfq',
40
41
  factor: Callable[[pd.DataFrame], pd.DataFrame] = lambda df: df,
41
- source: str = 'akshare',
42
+ source: str = 'baostock',
42
43
  pool_size: int = 10,
43
44
  datadir: Optional[Path] = None
44
45
  ):
@@ -50,7 +51,7 @@ class Data():
50
51
  period: 数据周期,如 '1d'(日线)、'1m'(分钟)
51
52
  adjust: 复权方式,'qfq'(前复权)、'hfq'(后复权)、'bfq'(不复权)
52
53
  factor: 因子计算函数,接收 DataFrame 返回 DataFrame,用于扩展自定义因子
53
- source: 数据来源,'akshare'(默认)或 'qmt'
54
+ source: 数据来源,'baostock'(默认)、'akshare''qmt'
54
55
  pool_size: 并发下载线程数
55
56
  datadir: 缓存根目录,默认为当前工作目录下的 datadir/
56
57
  """
@@ -90,6 +91,8 @@ class Data():
90
91
 
91
92
  if self.source == 'akshare':
92
93
  df = self._get_from_akshare(symbol)
94
+ elif self.source == 'baostock':
95
+ df = self._get_from_baostock(symbol)
93
96
  else:
94
97
  df = pd.DataFrame()
95
98
 
@@ -120,28 +123,46 @@ class Data():
120
123
 
121
124
  # 准备缓存目录
122
125
 
123
- with ThreadPoolExecutor(max_workers=self.pool_size) as executor:
124
- # 提交下载任务
125
- futures = {
126
- executor.submit(self._download, symbol): symbol
127
- for symbol in self.symbols
128
- }
129
-
130
- # 添加tqdm进度条
131
- errors = []
132
- with tqdm(total=len(self.symbols), desc="下载数据") as pbar:
133
- for future in as_completed(futures):
134
- symbol = futures[future]
126
+ # baostock 需要先登录,且其 C/S 架构不支持多线程并发
127
+ bs_logged_in = False
128
+ if self.source == 'baostock':
129
+ lg = bs.login()
130
+ if lg.error_code != '0':
131
+ raise RuntimeError(f"baostock 登录失败: {lg.error_msg}")
132
+ bs_logged_in = True
133
+
134
+ errors = []
135
+ try:
136
+ if self.source == 'baostock':
137
+ # baostock 串行下载(C/S 架构不支持并发)
138
+ for symbol in tqdm(self.symbols, desc="下载数据"):
135
139
  try:
136
- future.result() # 触发异常(如果有)
140
+ self._download(symbol)
137
141
  except Exception as e:
138
142
  errors.append(f"{symbol}: {e}")
139
143
  logger.warning(f"下载 {symbol} 失败: {e}")
140
- pbar.update(1)
141
- pbar.set_postfix_str(f"当前: {symbol}")
142
-
144
+ else:
145
+ # 其他数据源(akshare 等)并发下载
146
+ with ThreadPoolExecutor(max_workers=self.pool_size) as executor:
147
+ futures = {
148
+ executor.submit(self._download, symbol): symbol
149
+ for symbol in self.symbols
150
+ }
151
+ with tqdm(total=len(self.symbols), desc="下载数据") as pbar:
152
+ for future in as_completed(futures):
153
+ symbol = futures[future]
154
+ try:
155
+ future.result()
156
+ except Exception as e:
157
+ errors.append(f"{symbol}: {e}")
158
+ logger.warning(f"下载 {symbol} 失败: {e}")
159
+ pbar.update(1)
160
+ pbar.set_postfix_str(f"当前: {symbol}")
143
161
  if errors:
144
162
  logger.warning(f"共 {len(errors)} 只股票下载失败: {errors[:3]}...")
163
+ finally:
164
+ if bs_logged_in:
165
+ bs.logout()
145
166
 
146
167
  dfs = []
147
168
  for symbol in self.symbols:
@@ -209,3 +230,43 @@ class Data():
209
230
  df = df.set_index('date')
210
231
  # 设置索引
211
232
  return df
233
+
234
+ def _get_from_baostock(self, symbol: str) -> pd.DataFrame:
235
+ """
236
+ 从 baostock 获取单个股票的数据。
237
+
238
+ Args:
239
+ symbol (str): 股票代码,支持带后缀如 000001.SZ 或 600000.SH
240
+
241
+ Returns:
242
+ pd.DataFrame: 股票数据,以 date 为索引,包含 open, high, low, close, volume, amount 列
243
+ """
244
+ # baostock 代码格式:sz.000001 / sh.600000
245
+ bs_code = symbol.replace('.SZ', '.sz').replace('.SH', '.sh').replace('.BJ', '.bj')
246
+
247
+ # adjustflag: 1=不复权, 2=前复权, 3=后复权
248
+ adjust_map = {'bfq': '1', 'qfq': '2', 'hfq': '3'}
249
+ adjustflag = adjust_map.get(self.adjust, '2')
250
+
251
+ rs = bs.query_history_k_data_plus(
252
+ bs_code,
253
+ "date,open,high,low,close,volume,amount",
254
+ start_date='1990-01-01',
255
+ end_date='2050-12-31',
256
+ frequency='d',
257
+ adjustflag=adjustflag,
258
+ )
259
+ df = rs.get_data()
260
+
261
+ if len(df) == 0:
262
+ return df
263
+
264
+ # baostock 返回的数值列是字符串,转数值类型
265
+ numeric_cols = ["open", "high", "low", "close", "volume", "amount"]
266
+ for col in numeric_cols:
267
+ if col in df.columns:
268
+ df[col] = pd.to_numeric(df[col], errors="coerce")
269
+
270
+ df["date"] = pd.to_datetime(df["date"])
271
+ df = df.set_index("date")
272
+ return df
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes