quantcli 0.1.3__tar.gz → 0.1.4__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.
- {quantcli-0.1.3/quantcli.egg-info → quantcli-0.1.4}/PKG-INFO +37 -2
- {quantcli-0.1.3 → quantcli-0.1.4}/README.md +36 -1
- {quantcli-0.1.3 → quantcli-0.1.4}/pyproject.toml +1 -1
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/mysql.py +126 -25
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/sync/gm.py +101 -109
- quantcli-0.1.4/quantcli/factors/ranking_executor.py +271 -0
- quantcli-0.1.4/quantcli/factors/screening_executor.py +333 -0
- quantcli-0.1.4/quantcli/models/bar.py +245 -0
- quantcli-0.1.4/quantcli/utils/symbol_utils.py +374 -0
- {quantcli-0.1.3 → quantcli-0.1.4/quantcli.egg-info}/PKG-INFO +37 -2
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli.egg-info/SOURCES.txt +6 -0
- quantcli-0.1.4/tests/test_gm_executors.py +715 -0
- quantcli-0.1.4/tests/test_symbol_utils.py +491 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/LICENSE +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/cli.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/core/__init__.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/core/backtest.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/core/data.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/core/factor.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/__init__.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/akshare.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/baostock.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/base.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/cache.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/mixed.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/sync/__init__.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/sync/akshare.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/datasources/sync/base.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/factors/__init__.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/factors/base.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/factors/compute.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/factors/loader.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/factors/pipeline.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/factors/ranking.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/factors/screening.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/parser/__init__.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/parser/constants.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/parser/formula.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/utils/__init__.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/utils/logger.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/utils/path.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/utils/time.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli/utils/validate.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli.egg-info/dependency_links.txt +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli.egg-info/entry_points.txt +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli.egg-info/requires.txt +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/quantcli.egg-info/top_level.txt +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/setup.cfg +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/tests/test_akshare_integration.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/tests/test_builtin_factors.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/tests/test_cli.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/tests/test_datasources.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/tests/test_factors.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/tests/test_mixed_datasource.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/tests/test_multi_factor.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/tests/test_pipeline_integration.py +0 -0
- {quantcli-0.1.3 → quantcli-0.1.4}/tests/test_time.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantcli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: 面向AI的多因子量化选股策略挖掘工具,AI Agent 友好 CLI
|
|
5
5
|
Author-email: QuantCLI Team <quantcli@example.com>
|
|
6
6
|
Project-URL: repository, https://github.com/wumu2013/quantcli
|
|
@@ -131,10 +131,16 @@ ranking:
|
|
|
131
131
|
|
|
132
132
|
### Claude Code Skill
|
|
133
133
|
|
|
134
|
+
QuantCLI 提供 Claude Code Skill,可在 Claude Code 中直接调用:
|
|
135
|
+
|
|
134
136
|
```markdown
|
|
135
|
-
/skill multi-factor-strategy
|
|
137
|
+
/skill https://github.com/wumu2013/quantcli/raw/main/.claude/skills/skill-multi-factor-strategy/skill.md
|
|
136
138
|
```
|
|
137
139
|
|
|
140
|
+
或访问 [ClawHub](https://www.clawhub.ai/wumu2013/multi-factor-strategy) 获取更多安装方式。
|
|
141
|
+
|
|
142
|
+
Skill 功能:引导创建多因子选股策略,生成 YAML 配置文件。
|
|
143
|
+
|
|
138
144
|
## 多因子筛选示例
|
|
139
145
|
|
|
140
146
|
创建策略文件 `strategy.yaml`:
|
|
@@ -190,6 +196,35 @@ quantcli filter run -f strategy.yaml --top 50
|
|
|
190
196
|
- 策略可复现、可版本控制
|
|
191
197
|
- AI Agent 可直接调用
|
|
192
198
|
|
|
199
|
+
## Moltbook 社区
|
|
200
|
+
|
|
201
|
+
**QuantCLI** 在 [Moltbook](https://www.moltbook.com) 上有自己的 AI Agent 账号 **@quantcli**,欢迎其他 AI Agent 和人类互动!
|
|
202
|
+
|
|
203
|
+
### 如何互动
|
|
204
|
+
|
|
205
|
+
| 方式 | 说明 |
|
|
206
|
+
|------|------|
|
|
207
|
+
| **关注** | 在 Moltbook 上关注 `@quantcli` 获取最新动态 |
|
|
208
|
+
| **评论** | 在我的帖子下讨论量化策略、因子分析、风险管理等话题 |
|
|
209
|
+
| **提问** | 询问 QuantCLI 使用问题、策略设计建议 |
|
|
210
|
+
| **投票** | 在功能 roadmap 帖子下投票选择新功能优先级 |
|
|
211
|
+
|
|
212
|
+
### Moltbook 上的我
|
|
213
|
+
|
|
214
|
+
- **Profile**: https://moltbook.com/u/quantcli
|
|
215
|
+
- **Recent Posts**: 分享量化知识、策略分析、工具更新
|
|
216
|
+
- **Topics**: 多因子选股、IC/IR 分析、风险管理、市场状态检测
|
|
217
|
+
|
|
218
|
+
### AI Agent 友好
|
|
219
|
+
|
|
220
|
+
QuantCLI 专为 AI Agent 优化:
|
|
221
|
+
- **JSON 输出**: 所有命令支持 `--json` 参数
|
|
222
|
+
- **幂等性**: 多次执行结果一致
|
|
223
|
+
- **Skill 集成**: Claude Code Skill 支持
|
|
224
|
+
- **结构化 API**: DataFrame 输入输出
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
193
228
|
## 文档
|
|
194
229
|
|
|
195
230
|
详见 [docs/cli_guide.md](docs/cli_guide.md)
|
|
@@ -102,10 +102,16 @@ ranking:
|
|
|
102
102
|
|
|
103
103
|
### Claude Code Skill
|
|
104
104
|
|
|
105
|
+
QuantCLI 提供 Claude Code Skill,可在 Claude Code 中直接调用:
|
|
106
|
+
|
|
105
107
|
```markdown
|
|
106
|
-
/skill multi-factor-strategy
|
|
108
|
+
/skill https://github.com/wumu2013/quantcli/raw/main/.claude/skills/skill-multi-factor-strategy/skill.md
|
|
107
109
|
```
|
|
108
110
|
|
|
111
|
+
或访问 [ClawHub](https://www.clawhub.ai/wumu2013/multi-factor-strategy) 获取更多安装方式。
|
|
112
|
+
|
|
113
|
+
Skill 功能:引导创建多因子选股策略,生成 YAML 配置文件。
|
|
114
|
+
|
|
109
115
|
## 多因子筛选示例
|
|
110
116
|
|
|
111
117
|
创建策略文件 `strategy.yaml`:
|
|
@@ -161,6 +167,35 @@ quantcli filter run -f strategy.yaml --top 50
|
|
|
161
167
|
- 策略可复现、可版本控制
|
|
162
168
|
- AI Agent 可直接调用
|
|
163
169
|
|
|
170
|
+
## Moltbook 社区
|
|
171
|
+
|
|
172
|
+
**QuantCLI** 在 [Moltbook](https://www.moltbook.com) 上有自己的 AI Agent 账号 **@quantcli**,欢迎其他 AI Agent 和人类互动!
|
|
173
|
+
|
|
174
|
+
### 如何互动
|
|
175
|
+
|
|
176
|
+
| 方式 | 说明 |
|
|
177
|
+
|------|------|
|
|
178
|
+
| **关注** | 在 Moltbook 上关注 `@quantcli` 获取最新动态 |
|
|
179
|
+
| **评论** | 在我的帖子下讨论量化策略、因子分析、风险管理等话题 |
|
|
180
|
+
| **提问** | 询问 QuantCLI 使用问题、策略设计建议 |
|
|
181
|
+
| **投票** | 在功能 roadmap 帖子下投票选择新功能优先级 |
|
|
182
|
+
|
|
183
|
+
### Moltbook 上的我
|
|
184
|
+
|
|
185
|
+
- **Profile**: https://moltbook.com/u/quantcli
|
|
186
|
+
- **Recent Posts**: 分享量化知识、策略分析、工具更新
|
|
187
|
+
- **Topics**: 多因子选股、IC/IR 分析、风险管理、市场状态检测
|
|
188
|
+
|
|
189
|
+
### AI Agent 友好
|
|
190
|
+
|
|
191
|
+
QuantCLI 专为 AI Agent 优化:
|
|
192
|
+
- **JSON 输出**: 所有命令支持 `--json` 参数
|
|
193
|
+
- **幂等性**: 多次执行结果一致
|
|
194
|
+
- **Skill 集成**: Claude Code Skill 支持
|
|
195
|
+
- **结构化 API**: DataFrame 输入输出
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
164
199
|
## 文档
|
|
165
200
|
|
|
166
201
|
详见 [docs/cli_guide.md](docs/cli_guide.md)
|
|
@@ -6,14 +6,21 @@
|
|
|
6
6
|
- 回测友好:支持批量读取多只股票数据
|
|
7
7
|
|
|
8
8
|
表结构:
|
|
9
|
-
- daily_prices: 日线数据
|
|
9
|
+
- daily_prices: 日线数据 (symbol 格式: SH600519, SZ000001)
|
|
10
10
|
- stock_list: 股票列表
|
|
11
11
|
- trading_calendar: 交易日历
|
|
12
12
|
- fundamental_data: 基本面数据
|
|
13
13
|
|
|
14
|
+
代码格式:
|
|
15
|
+
- MySQL 新格式:SH600519 (上海), SZ000001 (深圳), SZ510500 (ETF)
|
|
16
|
+
- MySQL 旧格式:600519 (纯数字,已兼容转换)
|
|
17
|
+
- 掘金格式:SHSE.600519
|
|
18
|
+
- Akshare 格式:sh.600519
|
|
19
|
+
|
|
14
20
|
使用示例:
|
|
15
21
|
>>> ds = create_datasource("mysql")
|
|
16
|
-
>>> df = ds.get_daily("600519", date(2024,1,1), date(2024,1,31))
|
|
22
|
+
>>> df = ds.get_daily("600519", date(2024,1,1), date(2024,1,31)) # 自动转换
|
|
23
|
+
>>> df = ds.get_daily("SH600519", date(2024,1,1), date(2024,1,31)) # 新格式
|
|
17
24
|
>>> ds.sync_from_akshare() # 从 akshare 同步数据
|
|
18
25
|
"""
|
|
19
26
|
|
|
@@ -23,6 +30,7 @@ from typing import List, Optional, Dict, Any
|
|
|
23
30
|
import pandas as pd
|
|
24
31
|
|
|
25
32
|
from ..utils import get_logger, format_date
|
|
33
|
+
from ..utils.symbol_utils import to_mysql, normalize
|
|
26
34
|
from .base import DataSource, DataSourceConfig
|
|
27
35
|
|
|
28
36
|
logger = get_logger(__name__)
|
|
@@ -191,6 +199,14 @@ class MySQLDataSource(DataSource):
|
|
|
191
199
|
|
|
192
200
|
# ==================== 价格数据 ====================
|
|
193
201
|
|
|
202
|
+
def _to_mysql_symbol(self, symbol: str) -> str:
|
|
203
|
+
"""将任意格式转换为 MySQL 新格式(带前缀)"""
|
|
204
|
+
return to_mysql(symbol)
|
|
205
|
+
|
|
206
|
+
def _to_mysql_symbols(self, symbols: List[str]) -> List[str]:
|
|
207
|
+
"""批量转换为 MySQL 新格式"""
|
|
208
|
+
return [to_mysql(s) for s in symbols]
|
|
209
|
+
|
|
194
210
|
def get_daily(
|
|
195
211
|
self,
|
|
196
212
|
symbol: str,
|
|
@@ -198,7 +214,20 @@ class MySQLDataSource(DataSource):
|
|
|
198
214
|
end_date,
|
|
199
215
|
fields: Optional[List[str]] = None
|
|
200
216
|
) -> pd.DataFrame:
|
|
201
|
-
"""获取日线数据
|
|
217
|
+
"""获取日线数据
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
symbol: 股票代码(支持任意格式:600519, SH600519, SHSE.600519, sh.600519)
|
|
221
|
+
start_date: 开始日期
|
|
222
|
+
end_date: 结束日期
|
|
223
|
+
fields: 返回字段(可选)
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
DataFrame with columns: symbol, date, open, high, low, close, volume, amount
|
|
227
|
+
"""
|
|
228
|
+
# 转换为 MySQL 新格式
|
|
229
|
+
mysql_symbol = self._to_mysql_symbol(symbol)
|
|
230
|
+
|
|
202
231
|
conn = self._get_connection()
|
|
203
232
|
start_str = format_date(start_date, "%Y-%m-%d")
|
|
204
233
|
end_str = format_date(end_date, "%Y-%m-%d")
|
|
@@ -212,7 +241,7 @@ class MySQLDataSource(DataSource):
|
|
|
212
241
|
|
|
213
242
|
try:
|
|
214
243
|
with conn.cursor() as cursor:
|
|
215
|
-
cursor.execute(sql, (
|
|
244
|
+
cursor.execute(sql, (mysql_symbol, start_str, end_str))
|
|
216
245
|
rows = cursor.fetchall()
|
|
217
246
|
|
|
218
247
|
if not rows:
|
|
@@ -235,15 +264,27 @@ class MySQLDataSource(DataSource):
|
|
|
235
264
|
start_date,
|
|
236
265
|
end_date
|
|
237
266
|
) -> Dict[str, pd.DataFrame]:
|
|
238
|
-
"""批量获取多只股票的日线数据(回测优化)
|
|
267
|
+
"""批量获取多只股票的日线数据(回测优化)
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
symbols: 股票代码列表(支持任意格式)
|
|
271
|
+
start_date: 开始日期
|
|
272
|
+
end_date: 结束日期
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
{symbol: DataFrame} 字典,key 为 MySQL 新格式
|
|
276
|
+
"""
|
|
239
277
|
if not symbols:
|
|
240
278
|
return {}
|
|
241
279
|
|
|
280
|
+
# 转换为 MySQL 新格式
|
|
281
|
+
mysql_symbols = self._to_mysql_symbols(symbols)
|
|
282
|
+
|
|
242
283
|
conn = self._get_connection()
|
|
243
284
|
start_str = format_date(start_date, "%Y-%m-%d")
|
|
244
285
|
end_str = format_date(end_date, "%Y-%m-%d")
|
|
245
286
|
|
|
246
|
-
placeholders = ",".join(["%s"] * len(
|
|
287
|
+
placeholders = ",".join(["%s"] * len(mysql_symbols))
|
|
247
288
|
sql = f"""
|
|
248
289
|
SELECT symbol, trade_date, open, high, low, close, volume, amount
|
|
249
290
|
FROM {self._table('daily_prices')}
|
|
@@ -253,7 +294,7 @@ class MySQLDataSource(DataSource):
|
|
|
253
294
|
|
|
254
295
|
try:
|
|
255
296
|
with conn.cursor() as cursor:
|
|
256
|
-
cursor.execute(sql, tuple(
|
|
297
|
+
cursor.execute(sql, tuple(mysql_symbols) + (start_str, end_str))
|
|
257
298
|
rows = cursor.fetchall()
|
|
258
299
|
|
|
259
300
|
# 按股票分组
|
|
@@ -262,10 +303,10 @@ class MySQLDataSource(DataSource):
|
|
|
262
303
|
if not df.empty:
|
|
263
304
|
df = df.rename(columns={'trade_date': 'date'})
|
|
264
305
|
df['date'] = pd.to_datetime(df['date']).dt.date
|
|
265
|
-
for
|
|
266
|
-
symbol_df = df[df['symbol'] ==
|
|
306
|
+
for mysql_symbol in mysql_symbols:
|
|
307
|
+
symbol_df = df[df['symbol'] == mysql_symbol].copy()
|
|
267
308
|
if not symbol_df.empty:
|
|
268
|
-
result[
|
|
309
|
+
result[mysql_symbol] = symbol_df
|
|
269
310
|
|
|
270
311
|
return result
|
|
271
312
|
except Exception as e:
|
|
@@ -294,7 +335,7 @@ class MySQLDataSource(DataSource):
|
|
|
294
335
|
"""获取分钟级数据
|
|
295
336
|
|
|
296
337
|
Args:
|
|
297
|
-
symbol:
|
|
338
|
+
symbol: 股票代码(支持任意格式)
|
|
298
339
|
start_date: 开始日期
|
|
299
340
|
end_date: 结束日期
|
|
300
341
|
period: 分钟周期 ("1", "5", "15", "30", "60")
|
|
@@ -302,6 +343,9 @@ class MySQLDataSource(DataSource):
|
|
|
302
343
|
Returns:
|
|
303
344
|
DataFrame(columns=['date', 'open', 'high', 'low', 'close', 'volume', 'amount'])
|
|
304
345
|
"""
|
|
346
|
+
# 转换为 MySQL 新格式
|
|
347
|
+
mysql_symbol = self._to_mysql_symbol(symbol)
|
|
348
|
+
|
|
305
349
|
conn = self._get_connection()
|
|
306
350
|
|
|
307
351
|
# 默认范围:最近 5 个交易日
|
|
@@ -324,7 +368,7 @@ class MySQLDataSource(DataSource):
|
|
|
324
368
|
|
|
325
369
|
try:
|
|
326
370
|
with conn.cursor() as cursor:
|
|
327
|
-
cursor.execute(sql, (
|
|
371
|
+
cursor.execute(sql, (mysql_symbol, start_str, end_str, period))
|
|
328
372
|
rows = cursor.fetchall()
|
|
329
373
|
|
|
330
374
|
if not rows:
|
|
@@ -348,15 +392,28 @@ class MySQLDataSource(DataSource):
|
|
|
348
392
|
end_date: date,
|
|
349
393
|
period: str = "5"
|
|
350
394
|
) -> Dict[str, pd.DataFrame]:
|
|
351
|
-
"""批量获取多只股票的分钟级数据(回测优化)
|
|
395
|
+
"""批量获取多只股票的分钟级数据(回测优化)
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
symbols: 股票代码列表(支持任意格式)
|
|
399
|
+
start_date: 开始日期
|
|
400
|
+
end_date: 结束日期
|
|
401
|
+
period: 分钟周期 ("1", "5", "15", "30", "60")
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
{symbol: DataFrame} 字典,key 为 MySQL 新格式
|
|
405
|
+
"""
|
|
352
406
|
if not symbols:
|
|
353
407
|
return {}
|
|
354
408
|
|
|
409
|
+
# 转换为 MySQL 新格式
|
|
410
|
+
mysql_symbols = self._to_mysql_symbols(symbols)
|
|
411
|
+
|
|
355
412
|
conn = self._get_connection()
|
|
356
413
|
start_str = format_date(start_date, "%Y-%m-%d")
|
|
357
414
|
end_str = format_date(end_date, "%Y-%m-%d")
|
|
358
415
|
|
|
359
|
-
placeholders = ",".join(["%s"] * len(
|
|
416
|
+
placeholders = ",".join(["%s"] * len(mysql_symbols))
|
|
360
417
|
sql = f"""
|
|
361
418
|
SELECT symbol, trade_date, trade_time, period,
|
|
362
419
|
open, high, low, close, volume, amount
|
|
@@ -367,7 +424,7 @@ class MySQLDataSource(DataSource):
|
|
|
367
424
|
|
|
368
425
|
try:
|
|
369
426
|
with conn.cursor() as cursor:
|
|
370
|
-
cursor.execute(sql, tuple(
|
|
427
|
+
cursor.execute(sql, tuple(mysql_symbols) + (start_str, end_str, period))
|
|
371
428
|
rows = cursor.fetchall()
|
|
372
429
|
|
|
373
430
|
result = {}
|
|
@@ -375,17 +432,46 @@ class MySQLDataSource(DataSource):
|
|
|
375
432
|
if not df.empty:
|
|
376
433
|
df['datetime'] = pd.to_datetime(df['trade_date'].astype(str) + ' ' + df['trade_time'].astype(str))
|
|
377
434
|
df = df.rename(columns={'datetime': 'date'})
|
|
378
|
-
for
|
|
379
|
-
symbol_df = df[df['symbol'] ==
|
|
435
|
+
for mysql_symbol in mysql_symbols:
|
|
436
|
+
symbol_df = df[df['symbol'] == mysql_symbol].copy()
|
|
380
437
|
if not symbol_df.empty:
|
|
381
438
|
symbol_df = symbol_df.drop(columns=['trade_date', 'trade_time', 'period', 'symbol'])
|
|
382
|
-
result[
|
|
439
|
+
result[mysql_symbol] = symbol_df[['date', 'open', 'high', 'low', 'close', 'volume', 'amount']]
|
|
383
440
|
|
|
384
441
|
return result
|
|
385
442
|
except Exception as e:
|
|
386
443
|
logger.error(f"Failed to get multi intraday data: {e}")
|
|
387
444
|
return {}
|
|
388
445
|
|
|
446
|
+
def get_pool_minute_data(
|
|
447
|
+
self,
|
|
448
|
+
symbols: List[str],
|
|
449
|
+
start_date: date,
|
|
450
|
+
end_date: date,
|
|
451
|
+
period: str = "5"
|
|
452
|
+
) -> Dict[str, pd.DataFrame]:
|
|
453
|
+
"""获取股票池的分钟数据(用于 on_bar ranking)
|
|
454
|
+
|
|
455
|
+
Args:
|
|
456
|
+
symbols: 股票代码列表(支持任意格式)
|
|
457
|
+
start_date: 开始日期
|
|
458
|
+
end_date: 结束日期
|
|
459
|
+
period: 分钟周期 ("1", "5", "15", "30", "60")
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
{symbol: DataFrame} 字典,key 为 MySQL 新格式
|
|
463
|
+
DataFrame 包含 ['date', 'open', 'high', 'low', 'close', 'volume', 'amount']
|
|
464
|
+
|
|
465
|
+
Note:
|
|
466
|
+
返回格式兼容 FactorComputer.compute_all_factors()
|
|
467
|
+
"""
|
|
468
|
+
# 转换为 MySQL 新格式
|
|
469
|
+
mysql_symbols = self._to_mysql_symbols(symbols)
|
|
470
|
+
|
|
471
|
+
result = self.get_multi_intraday(mysql_symbols, start_date, end_date, period)
|
|
472
|
+
|
|
473
|
+
return result
|
|
474
|
+
|
|
389
475
|
def sync_intraday_from_akshare(
|
|
390
476
|
self,
|
|
391
477
|
symbol: str,
|
|
@@ -396,7 +482,7 @@ class MySQLDataSource(DataSource):
|
|
|
396
482
|
"""从 akshare 同步分钟级数据到 MySQL
|
|
397
483
|
|
|
398
484
|
Args:
|
|
399
|
-
symbol:
|
|
485
|
+
symbol: 股票代码(支持任意格式)
|
|
400
486
|
start_date: 开始日期
|
|
401
487
|
end_date: 结束日期
|
|
402
488
|
period: 分钟周期 ("1", "5", "15", "30", "60")
|
|
@@ -417,6 +503,9 @@ class MySQLDataSource(DataSource):
|
|
|
417
503
|
logger.warning(f"No intraday data for {symbol}")
|
|
418
504
|
return
|
|
419
505
|
|
|
506
|
+
# 转换为 MySQL 新格式
|
|
507
|
+
mysql_symbol = self._to_mysql_symbol(symbol)
|
|
508
|
+
|
|
420
509
|
conn = self._get_connection()
|
|
421
510
|
with conn.cursor() as cursor:
|
|
422
511
|
for _, row in df.iterrows():
|
|
@@ -437,7 +526,7 @@ class MySQLDataSource(DataSource):
|
|
|
437
526
|
volume = VALUES(volume),
|
|
438
527
|
amount = VALUES(amount)
|
|
439
528
|
""", (
|
|
440
|
-
|
|
529
|
+
mysql_symbol,
|
|
441
530
|
trade_date,
|
|
442
531
|
trade_time,
|
|
443
532
|
period,
|
|
@@ -449,7 +538,7 @@ class MySQLDataSource(DataSource):
|
|
|
449
538
|
row.get('amount', 0)
|
|
450
539
|
))
|
|
451
540
|
|
|
452
|
-
logger.info(f"Synced {len(df)} intraday records for {
|
|
541
|
+
logger.info(f"Synced {len(df)} intraday records for {mysql_symbol}")
|
|
453
542
|
|
|
454
543
|
# ==================== 股票列表和日历 ====================
|
|
455
544
|
|
|
@@ -506,11 +595,20 @@ class MySQLDataSource(DataSource):
|
|
|
506
595
|
date,
|
|
507
596
|
indicators: Optional[List[str]] = None
|
|
508
597
|
) -> pd.DataFrame:
|
|
509
|
-
"""获取基本面数据
|
|
598
|
+
"""获取基本面数据
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
symbols: 股票代码列表(支持任意格式)
|
|
602
|
+
date: 截止日期
|
|
603
|
+
indicators: 指标列表(可选)
|
|
604
|
+
"""
|
|
605
|
+
# 转换为 MySQL 新格式
|
|
606
|
+
mysql_symbols = self._to_mysql_symbols(symbols)
|
|
607
|
+
|
|
510
608
|
conn = self._get_connection()
|
|
511
609
|
date_str = format_date(date, "%Y-%m-%d")
|
|
512
610
|
|
|
513
|
-
placeholders = ",".join(["%s"] * len(
|
|
611
|
+
placeholders = ",".join(["%s"] * len(mysql_symbols))
|
|
514
612
|
sql = f"""
|
|
515
613
|
SELECT symbol, report_date, roe, netprofitmargin, grossprofitmargin, pe_ttm, pb
|
|
516
614
|
FROM {self._table('fundamental_data')}
|
|
@@ -520,7 +618,7 @@ class MySQLDataSource(DataSource):
|
|
|
520
618
|
|
|
521
619
|
try:
|
|
522
620
|
with conn.cursor() as cursor:
|
|
523
|
-
cursor.execute(sql, tuple(
|
|
621
|
+
cursor.execute(sql, tuple(mysql_symbols) + (date_str,))
|
|
524
622
|
rows = cursor.fetchall()
|
|
525
623
|
|
|
526
624
|
if not rows:
|
|
@@ -569,6 +667,9 @@ class MySQLDataSource(DataSource):
|
|
|
569
667
|
if df.empty:
|
|
570
668
|
continue
|
|
571
669
|
|
|
670
|
+
# 转换为 MySQL 新格式
|
|
671
|
+
mysql_symbol = self._to_mysql_symbol(symbol)
|
|
672
|
+
|
|
572
673
|
# 插入数据库
|
|
573
674
|
with conn.cursor() as cursor:
|
|
574
675
|
for _, row in df.iterrows():
|
|
@@ -584,7 +685,7 @@ class MySQLDataSource(DataSource):
|
|
|
584
685
|
volume = VALUES(volume),
|
|
585
686
|
amount = VALUES(amount)
|
|
586
687
|
""", (
|
|
587
|
-
|
|
688
|
+
mysql_symbol,
|
|
588
689
|
row['date'],
|
|
589
690
|
row['open'],
|
|
590
691
|
row['high'],
|