qka 1.2.1.dev6__tar.gz → 1.2.2.dev2__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.1.dev6 → qka-1.2.2.dev2}/PKG-INFO +3 -5
  2. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/README.md +2 -2
  3. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/pyproject.toml +0 -2
  4. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/broker.py +11 -2
  5. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/data.py +15 -7
  6. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/.github/workflows/docs.yml +0 -0
  7. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/.github/workflows/release.yml +0 -0
  8. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/.gitignore +0 -0
  9. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/.vscode/settings.json +0 -0
  10. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/CHANGELOG.md +0 -0
  11. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/LICENSE +0 -0
  12. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/api/brokers.md +0 -0
  13. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/api/core.md +0 -0
  14. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/api/utils.md +0 -0
  15. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/getting-started/concepts.md +0 -0
  16. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/getting-started/first-strategy.md +0 -0
  17. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/getting-started/installation.md +0 -0
  18. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/index.md +0 -0
  19. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/user-guide/backtest.md +0 -0
  20. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/user-guide/data.md +0 -0
  21. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/user-guide/trading.md +0 -0
  22. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/mkdocs.yml +0 -0
  23. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/__init__.py +0 -0
  24. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/brokers/__init__.py +0 -0
  25. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/brokers/client.py +0 -0
  26. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/brokers/server.py +0 -0
  27. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/brokers/trade.py +0 -0
  28. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/cli.py +0 -0
  29. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/__init__.py +0 -0
  30. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/backtest.py +0 -0
  31. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/report.py +0 -0
  32. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/strategy.py +0 -0
  33. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/mcp/__init__.py +0 -0
  34. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/mcp/api.py +0 -0
  35. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/mcp/server.py +0 -0
  36. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/__init__.py +0 -0
  37. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/handlers/__init__.py +0 -0
  38. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/handlers/class_inspector_handler.py +0 -0
  39. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/handlers/code_executor_handler.py +0 -0
  40. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/ws_client.py +0 -0
  41. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/zmq_server.py +0 -0
  42. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/utils/__init__.py +0 -0
  43. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/utils/anis.py +0 -0
  44. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/utils/logger.py +0 -0
  45. {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/utils/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qka
3
- Version: 1.2.1.dev6
3
+ Version: 1.2.2.dev2
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
@@ -46,12 +46,10 @@ Requires-Dist: mcp[cli]>=1.9.0
46
46
  Requires-Dist: nbformat>=5.10.4
47
47
  Requires-Dist: plotly>=6.1.1
48
48
  Requires-Dist: pyarrow>=21.0.0
49
- Requires-Dist: ta>=0.11.0
50
49
  Requires-Dist: tqdm>=4.67.1
51
50
  Requires-Dist: uvicorn>=0.34.3
52
51
  Requires-Dist: websockets>=16.0
53
52
  Requires-Dist: xtquant>=241014.1.2
54
- Requires-Dist: zmq>=0.0.0
55
53
  Provides-Extra: dev
56
54
  Requires-Dist: hatch-vcs>=0.3.0; extra == 'dev'
57
55
  Requires-Dist: mkdocs-material>=9.6.14; extra == 'dev'
@@ -126,8 +124,8 @@ df = data.get()
126
124
  class MyStrategy(qka.Strategy):
127
125
  def on_bar(self, date, get):
128
126
  close = get('close')
129
- # 价格低于10元买入1000
130
- if '000001.SZ' in close and close['000001.SZ'] < 10:
127
+ # 价格低于10元买入1000股(注意前复权可能导致早期价格为负)
128
+ if '000001.SZ' in close and 0 < close['000001.SZ'] < 10:
131
129
  self.broker.buy('000001.SZ', close['000001.SZ'], 1000)
132
130
  ```
133
131
 
@@ -61,8 +61,8 @@ df = data.get()
61
61
  class MyStrategy(qka.Strategy):
62
62
  def on_bar(self, date, get):
63
63
  close = get('close')
64
- # 价格低于10元买入1000
65
- if '000001.SZ' in close and close['000001.SZ'] < 10:
64
+ # 价格低于10元买入1000股(注意前复权可能导致早期价格为负)
65
+ if '000001.SZ' in close and 0 < close['000001.SZ'] < 10:
66
66
  self.broker.buy('000001.SZ', close['000001.SZ'], 1000)
67
67
  ```
68
68
 
@@ -30,8 +30,6 @@ dependencies = [
30
30
  "pyarrow>=21.0.0",
31
31
  "tqdm>=4.67.1",
32
32
  "dask[dataframe]>=2025.7.0",
33
- "ta>=0.11.0",
34
- "zmq>=0.0.0",
35
33
  "baostock>=0.8.9",
36
34
  "litellm>=1.81.14",
37
35
  "websockets>=16.0",
@@ -7,6 +7,7 @@ QKA经纪商模块
7
7
 
8
8
  import pandas as pd
9
9
  from typing import Any
10
+ from qka.utils.logger import logger
10
11
 
11
12
  # A 股默认费率
12
13
  DEFAULT_COMMISSION_RATE = 0.00025 # 万2.5 佣金
@@ -116,6 +117,10 @@ class Broker:
116
117
  print(f"买入数量必须大于 0!当前: {size}")
117
118
  return False
118
119
 
120
+ if price <= 0:
121
+ logger.warning(f"价格 {price:.2f} 不合法(前复权可能导致早期价格为负),跳过买入 {symbol}")
122
+ return False
123
+
119
124
  exec_price = price * (1 + self.slippage)
120
125
  amount = exec_price * size
121
126
  if self.commission_rate > 0:
@@ -150,7 +155,7 @@ class Broker:
150
155
  'timestamp': self.timestamp,
151
156
  })
152
157
 
153
- print(f"买入成功: {symbol} {size}股 @ {exec_price:.2f},花费 {total_cost:.2f}(佣金 {commission:.2f})")
158
+ logger.debug(f"买入成功: {symbol} {size}股 @ {exec_price:.2f},花费 {total_cost:.2f}(佣金 {commission:.2f})")
154
159
  return True
155
160
 
156
161
  def sell(self, symbol: str, price: float, size: int) -> bool:
@@ -171,6 +176,10 @@ class Broker:
171
176
  print(f"卖出数量必须大于 0!当前: {size}")
172
177
  return False
173
178
 
179
+ if price <= 0:
180
+ logger.warning(f"价格 {price:.2f} 不合法,跳过卖出 {symbol}")
181
+ return False
182
+
174
183
  if symbol not in self.positions:
175
184
  print(f"没有 {symbol} 的持仓!")
176
185
  return False
@@ -210,7 +219,7 @@ class Broker:
210
219
  'timestamp': self.timestamp,
211
220
  })
212
221
 
213
- print(f"卖出成功: {symbol} {size}股 @ {exec_price:.2f},获得 {net_proceeds:.2f}(佣金 {commission:.2f} + 印花税 {stamp_duty:.2f})")
222
+ logger.debug(f"卖出成功: {symbol} {size}股 @ {exec_price:.2f},获得 {net_proceeds:.2f}(佣金 {commission:.2f} + 印花税 {stamp_duty:.2f})")
214
223
  return True
215
224
 
216
225
  def get(self, factor: str, timestamp=None) -> Any:
@@ -101,18 +101,22 @@ class Data():
101
101
 
102
102
  return path
103
103
 
104
- def get(self) -> dd.DataFrame:
104
+ def get(self) -> pd.DataFrame:
105
105
  """
106
106
  获取历史数据
107
107
 
108
- 并发下载所有股票数据,应用因子计算,并返回合并后的Dask DataFrame。
108
+ 并发下载所有股票数据,应用因子计算,并返回合并后的 DataFrame。
109
109
 
110
110
  Returns:
111
- dd.DataFrame: 合并后的股票数据,每只股票的列名格式为 {symbol}_{column}
112
- 如果没有股票,返回空的 Dask DataFrame
111
+ pd.DataFrame: 合并后的股票数据,每只股票的列名格式为 {symbol}_{column}
112
+ 没有数据时抛出 RuntimeError
113
113
  """
114
114
  if not self.symbols:
115
- return dd.from_pandas(pd.DataFrame(), npartitions=1)
115
+ return pd.DataFrame()
116
+
117
+ # 缓存:避免重复读取
118
+ if hasattr(self, '_cached') and self._cached is not None:
119
+ return self._cached
116
120
 
117
121
  # 准备缓存目录
118
122
 
@@ -151,11 +155,15 @@ class Data():
151
155
  dfs.append(df.rename(columns=column_mapping))
152
156
 
153
157
  if not dfs:
154
- return dd.from_pandas(pd.DataFrame(), npartitions=1)
158
+ raise RuntimeError(
159
+ f"所有股票数据加载失败(共 {len(self.symbols)} 只),"
160
+ f"请检查网络连接和股票代码是否正确"
161
+ )
155
162
 
156
163
  df = dd.concat(dfs, axis=1, join='outer')
157
164
 
158
- return df
165
+ self._cached = df.compute()
166
+ return self._cached
159
167
 
160
168
  def _get_from_akshare(self, symbol: str) -> pd.DataFrame:
161
169
  """
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