qka 1.8.1.dev2__tar.gz → 1.9.1.dev4__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 (61) hide show
  1. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/PKG-INFO +60 -66
  2. qka-1.9.1.dev4/README.md +145 -0
  3. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/pyproject.toml +1 -18
  4. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/__init__.py +2 -7
  5. qka-1.9.1.dev4/qka/cli.py +21 -0
  6. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/core/backtest.py +44 -51
  7. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/core/data.py +151 -206
  8. qka-1.9.1.dev4/qka/core/strategy.py +71 -0
  9. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/utils/__init__.py +12 -14
  10. qka-1.9.1.dev4/skills/qka/SKILL.md +527 -0
  11. qka-1.8.1.dev2/.github/workflows/docs.yml +0 -75
  12. qka-1.8.1.dev2/README.md +0 -136
  13. qka-1.8.1.dev2/docs/advanced/performance.md +0 -203
  14. qka-1.8.1.dev2/docs/api/brokers.md +0 -4
  15. qka-1.8.1.dev2/docs/api/core.md +0 -58
  16. qka-1.8.1.dev2/docs/api/utils.md +0 -9
  17. qka-1.8.1.dev2/docs/examples/buy_and_hold.md +0 -77
  18. qka-1.8.1.dev2/docs/examples/ma_cross.md +0 -132
  19. qka-1.8.1.dev2/docs/examples/momentum.md +0 -81
  20. qka-1.8.1.dev2/docs/examples/multi_factor.md +0 -111
  21. qka-1.8.1.dev2/docs/examples/rsi_atr.md +0 -59
  22. qka-1.8.1.dev2/docs/guides/backtest.md +0 -48
  23. qka-1.8.1.dev2/docs/guides/data.md +0 -82
  24. qka-1.8.1.dev2/docs/guides/indicators.md +0 -78
  25. qka-1.8.1.dev2/docs/guides/report.md +0 -74
  26. qka-1.8.1.dev2/docs/guides/sizing.md +0 -83
  27. qka-1.8.1.dev2/docs/guides/strategy.md +0 -112
  28. qka-1.8.1.dev2/docs/guides/trading.md +0 -86
  29. qka-1.8.1.dev2/docs/index.md +0 -151
  30. qka-1.8.1.dev2/docs/user-guide/trading.md +0 -52
  31. qka-1.8.1.dev2/mkdocs.yml +0 -142
  32. qka-1.8.1.dev2/qka/brokers/__init__.py +0 -15
  33. qka-1.8.1.dev2/qka/brokers/client.py +0 -72
  34. qka-1.8.1.dev2/qka/brokers/server.py +0 -189
  35. qka-1.8.1.dev2/qka/brokers/trade.py +0 -176
  36. qka-1.8.1.dev2/qka/cli.py +0 -31
  37. qka-1.8.1.dev2/qka/core/strategy.py +0 -92
  38. qka-1.8.1.dev2/qka/mcp/__init__.py +0 -14
  39. qka-1.8.1.dev2/qka/mcp/api.py +0 -156
  40. qka-1.8.1.dev2/qka/mcp/server.py +0 -125
  41. qka-1.8.1.dev2/qka/server/__init__.py +0 -8
  42. qka-1.8.1.dev2/qka/server/handlers/__init__.py +0 -20
  43. qka-1.8.1.dev2/qka/server/handlers/class_inspector_handler.py +0 -68
  44. qka-1.8.1.dev2/qka/server/handlers/code_executor_handler.py +0 -91
  45. qka-1.8.1.dev2/qka/server/ws_client.py +0 -275
  46. qka-1.8.1.dev2/qka/server/zmq_server.py +0 -195
  47. qka-1.8.1.dev2/qka/utils/anis.py +0 -6
  48. qka-1.8.1.dev2/qka/utils/util.py +0 -50
  49. qka-1.8.1.dev2/skills/qka/SKILL.md +0 -578
  50. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/.github/workflows/release.yml +0 -0
  51. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/.gitignore +0 -0
  52. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/.vscode/settings.json +0 -0
  53. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/CHANGELOG.md +0 -0
  54. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/LICENSE +0 -0
  55. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/core/__init__.py +0 -0
  56. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/core/accessor.py +0 -0
  57. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/core/broker.py +0 -0
  58. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/core/report.py +0 -0
  59. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/core/sizing.py +0 -0
  60. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/qka/utils/logger.py +0 -0
  61. {qka-1.8.1.dev2 → qka-1.9.1.dev4}/tools/generate_api_ref.py +0 -0
@@ -1,9 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qka
3
- Version: 1.8.1.dev2
3
+ Version: 1.9.1.dev4
4
4
  Summary: QKA(快量化 / Quant Kit for A-shares)- 简洁易用的 A 股量化回测框架
5
5
  Project-URL: Home, https://github.com/zsrl/qka
6
- Project-URL: Documentation, https://zsrl.github.io/qka
7
6
  Project-URL: Repository, https://github.com/zsrl/qka
8
7
  Project-URL: Issues, https://github.com/zsrl/qka/issues
9
8
  Author-email: myc <mayuanchi1029@gmail.com>
@@ -35,28 +34,14 @@ Classifier: Programming Language :: Python :: 3.10
35
34
  Classifier: Programming Language :: Python :: 3.11
36
35
  Classifier: Programming Language :: Python :: 3.12
37
36
  Requires-Python: >=3.10
38
- Requires-Dist: akshare>=1.16.93
39
- Requires-Dist: baostock>=0.8.9
37
+ Requires-Dist: baostock>=0.9.1
40
38
  Requires-Dist: dask[dataframe]>=2025.7.0
41
- Requires-Dist: fastapi>=0.115.13
42
- Requires-Dist: flask>=3.1.1
43
- Requires-Dist: ipykernel>=6.29.5
44
- Requires-Dist: litellm>=1.81.14
45
- Requires-Dist: mcp[cli]>=1.9.0
46
- Requires-Dist: nbformat>=5.10.4
47
39
  Requires-Dist: plotly>=6.1.1
48
40
  Requires-Dist: pyarrow>=21.0.0
49
41
  Requires-Dist: ta>=0.11.0
50
42
  Requires-Dist: tqdm>=4.67.1
51
- Requires-Dist: uvicorn>=0.34.3
52
- Requires-Dist: websockets>=16.0
53
- Requires-Dist: xtquant>=241014.1.2
54
43
  Provides-Extra: dev
55
44
  Requires-Dist: hatch-vcs>=0.3.0; extra == 'dev'
56
- Requires-Dist: mkdocs-material>=9.6.14; extra == 'dev'
57
- Requires-Dist: mkdocs>=1.6.1; extra == 'dev'
58
- Requires-Dist: mkdocstrings-python>=1.16.12; extra == 'dev'
59
- Requires-Dist: mkdocstrings>=0.29.1; extra == 'dev'
60
45
  Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
61
46
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
62
47
  Requires-Dist: python-semantic-release>=9.0.0; extra == 'dev'
@@ -66,9 +51,6 @@ Description-Content-Type: text/markdown
66
51
  ## Quant Kit for A-shares
67
52
 
68
53
  <p align="center">
69
- <a href="https://qka.quantai.chat" target="_blank">
70
- <img src="https://img.shields.io/badge/文档站-qka.quantai.chat-blue?style=flat" alt="文档站">
71
- </a>
72
54
  <a href="https://pypi.org/project/qka/">
73
55
  <img src="https://img.shields.io/pypi/v/qka?color=blue" alt="PyPI">
74
56
  </a>
@@ -85,20 +67,40 @@ Description-Content-Type: text/markdown
85
67
  ```python
86
68
  from qka import Data, Strategy, Backtest
87
69
 
88
- class MyStrategy(Strategy):
70
+ data = Data(
71
+ symbols=['sz.000001'],
72
+ indicators={
73
+ 'sma_5': ('ta.trend.sma_indicator', 'close', 5),
74
+ 'sma_20': ('ta.trend.sma_indicator', 'close', 20),
75
+ },
76
+ )
77
+
78
+ class MaCross(Strategy):
79
+ def __init__(self):
80
+ super().__init__()
81
+ self.pct = 0.2
82
+
89
83
  def on_bar(self, date):
90
84
  close = self.get('close')
85
+ fast = self.get('sma_5')
86
+ slow = self.get('sma_20')
91
87
  for sym in close.index:
92
- if sym not in self.broker.positions:
93
- price = float(close[sym])
94
- if price > 0:
95
- size = self.sizing.percent(0.1, price)
96
- if size >= 100:
97
- self.broker.buy(sym, price, size)
98
-
99
- bt = Backtest(Data(['000001.SZ']), MyStrategy(cash=100_000))
100
- bt.run(benchmark='000300.SH')
101
- bt.report()
88
+ price = float(close[sym])
89
+ if price <= 0:
90
+ continue
91
+ if fast[sym] > slow[sym]:
92
+ size = self.sizing.percent(self.pct, price)
93
+ if size > 0:
94
+ self.broker.buy(sym, price, size)
95
+ else:
96
+ pos = self.broker.positions.get(sym, {}).get('size', 0)
97
+ if pos > 0:
98
+ self.broker.sell(sym, price, pos)
99
+
100
+ strategy = MaCross()
101
+ bt = Backtest(data, strategy)
102
+ bt.run(cash=200000, start_date='2024-01-01')
103
+ print(bt.metrics['total_return_pct'])
102
104
  ```
103
105
 
104
106
  ---
@@ -119,10 +121,13 @@ pip install qka
119
121
  from qka import Data
120
122
 
121
123
  data = Data(
122
- symbols=['000001.SZ', '600000.SH'],
123
- indicators={'sma_5': ('sma', 5), 'rsi_14': ('rsi', 14)},
124
+ symbols=['sz.000001', 'sh.600000'],
125
+ indicators={
126
+ 'sma_5': ('ta.trend.sma_indicator', 'close', 5),
127
+ 'rsi_14': ('ta.momentum.rsi', 'close', 14),
128
+ },
124
129
  )
125
- df = data.get() # 触发下载,返回宽表 DataFrame
130
+ df = data.get() # 返回宽表 DataFrame,列名 {symbol}|{factor}
126
131
  ```
127
132
 
128
133
  ### 策略
@@ -131,14 +136,15 @@ df = data.get() # 触发下载,返回宽表 DataFrame
131
136
  from qka import Strategy
132
137
 
133
138
  class MyStrategy(Strategy):
134
- def __init__(self, cash=100_000):
135
- super().__init__(cash=cash)
136
- # 自定义状态放这里
139
+ def __init__(self):
140
+ super().__init__()
141
+ self.lookback = 20 # 自定义参数
137
142
 
138
143
  def on_bar(self, date):
139
- close = self.get('close')
140
- hist = self.history('close', 20)
141
- # 写你的交易逻辑
144
+ close = self.get('close') # 当前横截面
145
+ hist = self.history('close', 20) # 历史窗口
146
+ # 交易逻辑:self.broker.buy / self.broker.sell
147
+ # 仓位计算:self.sizing.percent / self.sizing.fixed_shares
142
148
  ```
143
149
 
144
150
  ### 回测
@@ -146,37 +152,26 @@ class MyStrategy(Strategy):
146
152
  ```python
147
153
  from qka import Backtest
148
154
 
149
- bt = Backtest(data, MyStrategy(cash=100_000))
150
- bt.run(benchmark='000300.SH')
151
- print(bt.summary()) # 输出绩效指标
152
- bt.report() # 生成 HTML 报告
155
+ strategy = MyStrategy()
156
+ bt = Backtest(data, strategy)
157
+ bt.run(cash=200000, start_date='2024-01-01', benchmark='sh.000300')
158
+ print(bt.metrics['total_return_pct']) # 总收益率
159
+ print(bt.metrics['sharpe_ratio']) # 夏普比率
153
160
  ```
154
161
 
155
- ### 更多示例
156
-
157
- | 策略 | 说明 |
158
- |------|------|
159
- | [买入持有与定投](https://qka.quantai.chat/examples/buy_and_hold/) | 买入不动 + 每月定投 |
160
- | [均线交叉](https://qka.quantai.chat/examples/ma_cross/) | 5日线上穿/下穿20日线 |
161
- | [RSI + ATR 风控](https://qka.quantai.chat/examples/rsi_atr/) | RSI 超卖买入,ATR 止损 |
162
- | [动量排序选股](https://qka.quantai.chat/examples/momentum/) | 月度动量排序,Top 5 等权 |
163
- | [多因子打分](https://qka.quantai.chat/examples/multi_factor/) | PE/ROE/动量/波动率打分选股 |
164
-
165
162
  ## 核心能力
166
163
 
167
- - **多数据源** — baostock(默认)、akshare、QMT,自动缓存
168
- - **预计算指标** — sma/ema/macd/rsi/bbands/atr + 自定义因子
164
+ - **多数据源** — baostock(默认)、akshare、QMT
165
+ - **预计算指标** — ta 库全部 60+ 指标,`('ta.trend.sma_indicator', 'close', 5)` 格式直接透传
169
166
  - **事件驱动回测** — 按日推进,`self.get()` 横截面 + `self.history()` 窗口序列
170
- - **仓位管理** — `self.sizing.percent()` / `self.sizing.fixed_amount()` / `self.sizing.fixed_shares()` / `self.sizing.atr_risk()`
171
- - **交易模拟** — 佣金万2.5、印花税万5、滑点0.1%,最低佣金5元
172
- - **HTML 报告** Plotly 交互图表,累计收益、回撤、月度热力图、交易明细
173
- - **基准对比** — 自动下载沪深300(或指定指数)做对比
167
+ - **仓位管理** — `sizing.percent()` / `sizing.fixed_amount()` / `sizing.fixed_shares()` / `sizing.atr_risk()`
168
+ - **交易模拟** — 佣金万 2.5、印花税万 5(仅卖出)、滑点 0.1%,最低佣金 5
169
+ - **绩效指标**总收益率、年化、夏普比率、最大回撤、Calmar、胜率、盈亏比等 13 项
170
+ - **基准对比** — 支持沪深 300(或指定指数)对比
174
171
 
175
172
  ## 文档
176
173
 
177
- 完整教程、API 参考、示例代码:
178
-
179
- 👉 **[qka.quantai.chat](https://qka.quantai.chat)**
174
+ 框架 API 完整文档见 [skills/qka/SKILL.md](skills/qka/SKILL.md)——所有类的方法签名、参数、返回值和约束都在里面。
180
175
 
181
176
  ## 下一步规划
182
177
 
@@ -191,10 +186,9 @@ bt.report() # 生成 HTML 报告
191
186
  ## 致谢
192
187
 
193
188
  - [baostock](http://baostock.com) — 免费 A 股数据
194
- - [Akshare](https://github.com/akfamily/akshare) — 补充数据源
195
- - [Plotly](https://plotly.com/python/) — 交互式图表
196
- - [xtquant](https://github.com/ShiMiaoYS/xtquant) — QMT 接口
189
+ - [ta](https://github.com/bukosabino/ta) — 技术指标库
197
190
 
198
191
  ---
199
192
 
200
193
  > ⚠️ 量化交易存在风险,请充分了解后再使用本框架。
194
+ 充分了解后再使用本框架。
@@ -0,0 +1,145 @@
1
+ # QKA — 快量化
2
+ ## Quant Kit for A-shares
3
+
4
+ <p align="center">
5
+ <a href="https://pypi.org/project/qka/">
6
+ <img src="https://img.shields.io/pypi/v/qka?color=blue" alt="PyPI">
7
+ </a>
8
+ <a href="https://github.com/zsrl/qka">
9
+ <img src="https://img.shields.io/badge/python-3.10+-blue" alt="Python">
10
+ </a>
11
+ <a href="LICENSE">
12
+ <img src="https://img.shields.io/badge/license-MIT-green" alt="License">
13
+ </a>
14
+ </p>
15
+
16
+ **QKA(快量化 / Quant Kit for A-shares)** — 简洁易用的 A 股量化回测框架。
17
+
18
+ ```python
19
+ from qka import Data, Strategy, Backtest
20
+
21
+ data = Data(
22
+ symbols=['sz.000001'],
23
+ indicators={
24
+ 'sma_5': ('ta.trend.sma_indicator', 'close', 5),
25
+ 'sma_20': ('ta.trend.sma_indicator', 'close', 20),
26
+ },
27
+ )
28
+
29
+ class MaCross(Strategy):
30
+ def __init__(self):
31
+ super().__init__()
32
+ self.pct = 0.2
33
+
34
+ def on_bar(self, date):
35
+ close = self.get('close')
36
+ fast = self.get('sma_5')
37
+ slow = self.get('sma_20')
38
+ for sym in close.index:
39
+ price = float(close[sym])
40
+ if price <= 0:
41
+ continue
42
+ if fast[sym] > slow[sym]:
43
+ size = self.sizing.percent(self.pct, price)
44
+ if size > 0:
45
+ self.broker.buy(sym, price, size)
46
+ else:
47
+ pos = self.broker.positions.get(sym, {}).get('size', 0)
48
+ if pos > 0:
49
+ self.broker.sell(sym, price, pos)
50
+
51
+ strategy = MaCross()
52
+ bt = Backtest(data, strategy)
53
+ bt.run(cash=200000, start_date='2024-01-01')
54
+ print(bt.metrics['total_return_pct'])
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 安装
60
+
61
+ ```bash
62
+ pip install qka
63
+ ```
64
+
65
+ 需要 Python 3.10+。
66
+
67
+ ## 快速上手
68
+
69
+ ### 数据
70
+
71
+ ```python
72
+ from qka import Data
73
+
74
+ data = Data(
75
+ symbols=['sz.000001', 'sh.600000'],
76
+ indicators={
77
+ 'sma_5': ('ta.trend.sma_indicator', 'close', 5),
78
+ 'rsi_14': ('ta.momentum.rsi', 'close', 14),
79
+ },
80
+ )
81
+ df = data.get() # 返回宽表 DataFrame,列名 {symbol}|{factor}
82
+ ```
83
+
84
+ ### 策略
85
+
86
+ ```python
87
+ from qka import Strategy
88
+
89
+ class MyStrategy(Strategy):
90
+ def __init__(self):
91
+ super().__init__()
92
+ self.lookback = 20 # 自定义参数
93
+
94
+ def on_bar(self, date):
95
+ close = self.get('close') # 当前横截面
96
+ hist = self.history('close', 20) # 历史窗口
97
+ # 交易逻辑:self.broker.buy / self.broker.sell
98
+ # 仓位计算:self.sizing.percent / self.sizing.fixed_shares
99
+ ```
100
+
101
+ ### 回测
102
+
103
+ ```python
104
+ from qka import Backtest
105
+
106
+ strategy = MyStrategy()
107
+ bt = Backtest(data, strategy)
108
+ bt.run(cash=200000, start_date='2024-01-01', benchmark='sh.000300')
109
+ print(bt.metrics['total_return_pct']) # 总收益率
110
+ print(bt.metrics['sharpe_ratio']) # 夏普比率
111
+ ```
112
+
113
+ ## 核心能力
114
+
115
+ - **多数据源** — baostock(默认)、akshare、QMT
116
+ - **预计算指标** — ta 库全部 60+ 指标,`('ta.trend.sma_indicator', 'close', 5)` 格式直接透传
117
+ - **事件驱动回测** — 按日推进,`self.get()` 横截面 + `self.history()` 窗口序列
118
+ - **仓位管理** — `sizing.percent()` / `sizing.fixed_amount()` / `sizing.fixed_shares()` / `sizing.atr_risk()`
119
+ - **交易模拟** — 佣金万 2.5、印花税万 5(仅卖出)、滑点 0.1%,最低佣金 5 元
120
+ - **绩效指标** — 总收益率、年化、夏普比率、最大回撤、Calmar、胜率、盈亏比等 13 项
121
+ - **基准对比** — 支持沪深 300(或指定指数)对比
122
+
123
+ ## 文档
124
+
125
+ 框架 API 完整文档见 [skills/qka/SKILL.md](skills/qka/SKILL.md)——所有类的方法签名、参数、返回值和约束都在里面。
126
+
127
+ ## 下一步规划
128
+
129
+ - [ ] 分钟级数据支持
130
+ - [ ] 自适应参数优化
131
+ - [ ] 实盘交易(QMT 接口)
132
+
133
+ ## 许可证
134
+
135
+ [MIT](LICENSE)
136
+
137
+ ## 致谢
138
+
139
+ - [baostock](http://baostock.com) — 免费 A 股数据
140
+ - [ta](https://github.com/bukosabino/ta) — 技术指标库
141
+
142
+ ---
143
+
144
+ > ⚠️ 量化交易存在风险,请充分了解后再使用本框架。
145
+ 充分了解后再使用本框架。
@@ -18,30 +18,16 @@ classifiers = [
18
18
  ]
19
19
  requires-python = ">=3.10"
20
20
  dependencies = [
21
- "akshare>=1.16.93",
22
- "fastapi>=0.115.13",
23
- "flask>=3.1.1",
24
- "mcp[cli]>=1.9.0",
25
- "nbformat>=5.10.4",
26
21
  "plotly>=6.1.1",
27
- "uvicorn>=0.34.3",
28
- "xtquant>=241014.1.2",
29
- "ipykernel>=6.29.5",
30
22
  "pyarrow>=21.0.0",
31
23
  "tqdm>=4.67.1",
32
24
  "dask[dataframe]>=2025.7.0",
33
- "baostock>=0.8.9",
34
- "litellm>=1.81.14",
35
- "websockets>=16.0",
36
25
  "ta>=0.11.0",
26
+ "baostock>=0.9.1",
37
27
  ]
38
28
 
39
29
  [project.optional-dependencies]
40
30
  dev = [
41
- "mkdocs>=1.6.1",
42
- "mkdocs-material>=9.6.14",
43
- "mkdocstrings>=0.29.1",
44
- "mkdocstrings-python>=1.16.12",
45
31
  "python-semantic-release>=9.0.0",
46
32
  "pytest>=7.0.0",
47
33
  "pytest-cov>=4.0.0",
@@ -50,14 +36,11 @@ dev = [
50
36
 
51
37
  [project.urls]
52
38
  Home = "https://github.com/zsrl/qka"
53
- Documentation = "https://zsrl.github.io/qka"
54
39
  Repository = "https://github.com/zsrl/qka"
55
40
  Issues = "https://github.com/zsrl/qka/issues"
56
41
 
57
42
  [project.scripts]
58
- qka-zmq-server = "qka.server.zmq_server:main"
59
43
  qka = "qka.cli:main"
60
- qka-ws-client = "qka.server.ws_client:main"
61
44
 
62
45
  [tool.hatch.version]
63
46
  source = "vcs"
@@ -20,16 +20,11 @@ from qka.core.broker import Broker
20
20
  from qka.core.sizing import SizingAccessor
21
21
 
22
22
  # 子模块导入
23
- from qka import core, utils, mcp
24
-
25
- # 交易相关(有依赖的模块暂时不导入,避免导入错误)
26
- # from qka.brokers.trade import create_trader
27
- # from qka.brokers.client import QMTClient
28
- # from qka.brokers.server import QMTServer
23
+ from qka import core, utils
29
24
 
30
25
  __all__ = [
31
26
  # 核心功能
32
27
  'Data', 'Backtest', 'Strategy', 'Broker', 'DataAccessor', 'SizingAccessor',
33
28
  # 子模块
34
- 'core', 'utils', 'mcp'
29
+ 'core', 'utils'
35
30
  ]
@@ -0,0 +1,21 @@
1
+ """
2
+ QKA命令行接口
3
+ """
4
+ import argparse
5
+ import sys
6
+
7
+
8
+ def main():
9
+ parser = argparse.ArgumentParser(description="QKA量化交易框架命令行工具")
10
+ parser.add_argument("--version", action="store_true", help="显示版本信息")
11
+
12
+ args = parser.parse_args()
13
+ if args.version:
14
+ from qka import __version__
15
+ print(f"qka {__version__}")
16
+ else:
17
+ parser.print_help()
18
+
19
+
20
+ if __name__ == "__main__":
21
+ main()
@@ -9,6 +9,9 @@ import numpy as np
9
9
  import dask.dataframe as dd
10
10
  from typing import Optional, Union
11
11
  from collections import defaultdict
12
+ from qka.core.broker import Broker
13
+ from qka.core.accessor import DataAccessor
14
+ from qka.core.sizing import SizingAccessor
12
15
 
13
16
 
14
17
  class Backtest:
@@ -36,9 +39,20 @@ class Backtest:
36
39
  self.data = data
37
40
  self.strategy = strategy
38
41
  self.results = None
39
- self.initial_cash = strategy.broker.cash
42
+ self.initial_cash = None # 由 run() 设置
43
+ self.metrics = None
40
44
  self._benchmark_data = None
41
45
 
46
+ @property
47
+ def trade_history(self):
48
+ """逐笔交易明细(list[dict])"""
49
+ return self.strategy.broker.trade_history if hasattr(self.strategy, 'broker') else []
50
+
51
+ @property
52
+ def trades(self):
53
+ """每日资金/市值/持仓快照(pd.DataFrame)"""
54
+ return self.strategy.broker.trades if hasattr(self.strategy, 'broker') else pd.DataFrame()
55
+
42
56
  @staticmethod
43
57
  def _parse_row(row):
44
58
  """
@@ -63,7 +77,9 @@ class Backtest:
63
77
  by_factor[factor][symbol] = val
64
78
  return dict(by_factor)
65
79
 
66
- def run(self, benchmark: Optional[str] = None):
80
+ def run(self, cash: float = 100000.0,
81
+ start_date: str = None, end_date: str = None,
82
+ benchmark: Optional[str] = None):
67
83
  """
68
84
  执行回测
69
85
 
@@ -74,15 +90,23 @@ class Backtest:
74
90
  避免一次性加载全量数据。
75
91
 
76
92
  Args:
77
- benchmark (str, optional): 基准代码,如 '000300.SH'(沪深300)。
78
- 如果提供,会下载基准数据用于对比。
93
+ cash: 初始资金,默认 10 万元
94
+ start_date: 回测起始日期 YYYY-MM-DD,None 表示数据最早日期
95
+ end_date: 回测截止日期 YYYY-MM-DD,None 表示数据最晚日期
96
+ benchmark: 基准代码,如 '000300.SH'(沪深300)。
97
+ 如果提供,会下载基准数据用于对比。
79
98
 
80
99
  Returns:
81
- None。回测结果保存在 self.results 中,可通过
82
- self.summary() 查看绩效指标,self.report() 生成报告。
100
+ None。回测结果保存在 self.results、self.metrics、self.trade_history 中。
83
101
  """
84
- # 获取数据(优先用 lazy 模式,由 Backtest 决定是否分区)
85
- raw = self.data.get(lazy=True)
102
+ # 注入基础设施
103
+ self.initial_cash = cash
104
+ self.strategy.broker = Broker(initial_cash=cash)
105
+ self.strategy.sizing = SizingAccessor(self.strategy.broker)
106
+ self.strategy._data = DataAccessor()
107
+
108
+ # 获取数据
109
+ raw = self.data.get(lazy=True, start_date=start_date, end_date=end_date)
86
110
 
87
111
  # 加载基准数据
88
112
  if benchmark:
@@ -137,6 +161,7 @@ class Backtest:
137
161
 
138
162
  # 保存回测结果
139
163
  self.results = self.strategy.broker.trades
164
+ self.metrics = self._compute_metrics()
140
165
 
141
166
  def _load_benchmark(self, benchmark_code: str):
142
167
  """
@@ -146,38 +171,27 @@ class Backtest:
146
171
  benchmark_code: 基准代码,如 '000300.SH'
147
172
  """
148
173
  try:
149
- import akshare as ak
150
- clean_code = benchmark_code.replace('.SH', '').replace('.SZ', '').replace('.BJ', '')
151
- bm_df = ak.stock_zh_index_daily(symbol=f"sh{clean_code}")
174
+ from qka.core.data import Data
175
+ bm_data = Data(symbols=[benchmark_code], source='baostock')
176
+ bm_df = bm_data.get(lazy=False)
152
177
  if bm_df is not None and not bm_df.empty:
153
- bm_df['date'] = pd.to_datetime(bm_df['date'])
154
- bm_df = bm_df.set_index('date')
155
- bm_df = bm_df.sort_index()
156
- self._benchmark_data = bm_df['close']
178
+ # 提取 close 列(可能是多股票 MultiIndex,取第一只)
179
+ close_col = [c for c in bm_df.columns if 'close' in str(c).lower()]
180
+ if close_col:
181
+ self._benchmark_data = bm_df[close_col[0]]
182
+ else:
183
+ self._benchmark_data = bm_df.iloc[:, 0]
157
184
  print(f"基准数据加载成功: {benchmark_code},{len(bm_df)} 个交易日")
158
185
  except Exception as e:
159
186
  print(f"基准数据加载失败: {e}")
160
187
 
161
- def summary(self) -> dict:
162
- """
163
- 计算并打印回测绩效指标
164
-
165
- 返回包含以下指标的字典:
166
- - 总收益率、年化收益率、年化波动率
167
- - 夏普比率、最大回撤、Calmar比率
168
- - 胜率、盈亏比、交易次数
169
- - 最终资产、总手续费
170
-
171
- Returns:
172
- dict: 绩效指标字典
173
- """
188
+ def _compute_metrics(self) -> dict:
189
+ """计算绩效指标。无数据时返回空 dict。"""
174
190
  if self.results is None or self.results.empty:
175
- print("请先运行回测 (backtest.run())")
176
191
  return {}
177
192
 
178
193
  totals = self.results['total']
179
194
  if len(totals) < 2:
180
- print("回测数据不足(至少需要2个交易周期)")
181
195
  return {}
182
196
 
183
197
  # 基本数据
@@ -192,7 +206,6 @@ class Backtest:
192
206
  # 日收益率序列
193
207
  daily_returns = totals.pct_change().dropna()
194
208
  if len(daily_returns) == 0:
195
- print("没有足够的收益率数据")
196
209
  return {}
197
210
 
198
211
  # 年化收益率
@@ -257,30 +270,10 @@ class Backtest:
257
270
  else:
258
271
  win_rate = 0
259
272
  profit_loss_ratio = 0
260
- trade_pnl = []
261
273
 
262
274
  # 总手续费
263
275
  total_commission = self.strategy.broker.total_commission
264
276
 
265
- # 打印报告
266
- print("=" * 55)
267
- print(" 回测绩效报告")
268
- print("=" * 55)
269
- print(f" 初始资金: RMB {initial:>10,.2f}")
270
- print(f" 最终资产: RMB {final:>10,.2f}")
271
- print(f" 总收益率: {total_return:>+8.2f}%")
272
- print(f" 年化收益率: {annual_return * 100:>+8.2f}%")
273
- print(f" 年化波动率: {annual_vol * 100:>8.2f}%")
274
- print(f" 夏普比率: {sharpe:>8.2f}")
275
- print(f" 最大回撤: {max_drawdown:>8.2f}%")
276
- print(f" Calmar比率: {calmar:>8.2f}")
277
- print(f" 交易次数: {n_trades:>8}")
278
- print(f" 胜率: {win_rate:>8.2f}%")
279
- print(f" 盈亏比: {profit_loss_ratio:>8.2f}")
280
- print(f" 总手续费: RMB {total_commission:>10,.2f}")
281
- print(f" 回测天数: {n_days:>8} 天")
282
- print("=" * 55)
283
-
284
277
  return {
285
278
  'initial_cash': initial,
286
279
  'final_equity': final,