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.
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/PKG-INFO +3 -5
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/README.md +2 -2
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/pyproject.toml +0 -2
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/broker.py +11 -2
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/data.py +15 -7
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/.github/workflows/docs.yml +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/.github/workflows/release.yml +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/.gitignore +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/.vscode/settings.json +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/CHANGELOG.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/LICENSE +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/api/brokers.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/api/core.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/api/utils.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/getting-started/concepts.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/getting-started/first-strategy.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/getting-started/installation.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/index.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/user-guide/backtest.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/user-guide/data.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/docs/user-guide/trading.md +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/mkdocs.yml +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/__init__.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/brokers/__init__.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/brokers/client.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/brokers/server.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/brokers/trade.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/cli.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/__init__.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/backtest.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/report.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/core/strategy.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/mcp/__init__.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/mcp/api.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/mcp/server.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/__init__.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/handlers/__init__.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/handlers/class_inspector_handler.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/handlers/code_executor_handler.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/ws_client.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/server/zmq_server.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/utils/__init__.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/utils/anis.py +0 -0
- {qka-1.2.1.dev6 → qka-1.2.2.dev2}/qka/utils/logger.py +0 -0
- {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.
|
|
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
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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) ->
|
|
104
|
+
def get(self) -> pd.DataFrame:
|
|
105
105
|
"""
|
|
106
106
|
获取历史数据
|
|
107
107
|
|
|
108
|
-
并发下载所有股票数据,应用因子计算,并返回合并后的
|
|
108
|
+
并发下载所有股票数据,应用因子计算,并返回合并后的 DataFrame。
|
|
109
109
|
|
|
110
110
|
Returns:
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
pd.DataFrame: 合并后的股票数据,每只股票的列名格式为 {symbol}_{column}
|
|
112
|
+
没有数据时抛出 RuntimeError
|
|
113
113
|
"""
|
|
114
114
|
if not self.symbols:
|
|
115
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|