quantcli 0.2.2__tar.gz → 0.2.5__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 (102) hide show
  1. {quantcli-0.2.2/quantcli.egg-info → quantcli-0.2.5}/PKG-INFO +1 -1
  2. {quantcli-0.2.2 → quantcli-0.2.5}/pyproject.toml +1 -1
  3. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/cli.py +99 -13
  4. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/mysql.py +11 -4
  5. {quantcli-0.2.2 → quantcli-0.2.5/quantcli.egg-info}/PKG-INFO +1 -1
  6. {quantcli-0.2.2 → quantcli-0.2.5}/LICENSE +0 -0
  7. {quantcli-0.2.2 → quantcli-0.2.5}/README.md +0 -0
  8. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/core/__init__.py +0 -0
  9. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/core/backtest.py +0 -0
  10. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/core/data.py +0 -0
  11. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/core/factor.py +0 -0
  12. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/__init__.py +0 -0
  13. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/akshare.py +0 -0
  14. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/baostock.py +0 -0
  15. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/base.py +0 -0
  16. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/cache.py +0 -0
  17. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/fundamentals/__init__.py +0 -0
  18. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/fundamentals/provider.py +0 -0
  19. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/mixed.py +0 -0
  20. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/sync/__init__.py +0 -0
  21. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/sync/akshare.py +0 -0
  22. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/sync/base.py +0 -0
  23. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/sync/gm.py +0 -0
  24. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/datasources/sync/gm_fundamental.py +0 -0
  25. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/__init__.py +0 -0
  26. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_001.yaml +0 -0
  27. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_002.yaml +0 -0
  28. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_003.yaml +0 -0
  29. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_004.yaml +0 -0
  30. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_005.yaml +0 -0
  31. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_006.yaml +0 -0
  32. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_007.yaml +0 -0
  33. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_008.yaml +0 -0
  34. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_009.yaml +0 -0
  35. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_010.yaml +0 -0
  36. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_011.yaml +0 -0
  37. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_012.yaml +0 -0
  38. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_013.yaml +0 -0
  39. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_014.yaml +0 -0
  40. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_015.yaml +0 -0
  41. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_016.yaml +0 -0
  42. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_017.yaml +0 -0
  43. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_018.yaml +0 -0
  44. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_019.yaml +0 -0
  45. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_020.yaml +0 -0
  46. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_021.yaml +0 -0
  47. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_022.yaml +0 -0
  48. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_023.yaml +0 -0
  49. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_024.yaml +0 -0
  50. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_025.yaml +0 -0
  51. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_026.yaml +0 -0
  52. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_027.yaml +0 -0
  53. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_028.yaml +0 -0
  54. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_029.yaml +0 -0
  55. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_030.yaml +0 -0
  56. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_031.yaml +0 -0
  57. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_032.yaml +0 -0
  58. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_033.yaml +0 -0
  59. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_034.yaml +0 -0
  60. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_035.yaml +0 -0
  61. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_036.yaml +0 -0
  62. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_037.yaml +0 -0
  63. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_038.yaml +0 -0
  64. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_039.yaml +0 -0
  65. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/alpha101/alpha_040.yaml +0 -0
  66. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/base.py +0 -0
  67. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/compute.py +0 -0
  68. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/loader.py +0 -0
  69. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/pipeline.py +0 -0
  70. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/ranking.py +0 -0
  71. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/ranking_executor.py +0 -0
  72. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/screening.py +0 -0
  73. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/factors/screening_executor.py +0 -0
  74. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/models/bar.py +0 -0
  75. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/parser/__init__.py +0 -0
  76. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/parser/constants.py +0 -0
  77. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/parser/formula.py +0 -0
  78. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/utils/__init__.py +0 -0
  79. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/utils/env.py +0 -0
  80. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/utils/logger.py +0 -0
  81. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/utils/path.py +0 -0
  82. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/utils/symbol_utils.py +0 -0
  83. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/utils/time.py +0 -0
  84. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli/utils/validate.py +0 -0
  85. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli.egg-info/SOURCES.txt +0 -0
  86. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli.egg-info/dependency_links.txt +0 -0
  87. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli.egg-info/entry_points.txt +0 -0
  88. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli.egg-info/requires.txt +0 -0
  89. {quantcli-0.2.2 → quantcli-0.2.5}/quantcli.egg-info/top_level.txt +0 -0
  90. {quantcli-0.2.2 → quantcli-0.2.5}/setup.cfg +0 -0
  91. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_akshare_integration.py +0 -0
  92. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_builtin_factors.py +0 -0
  93. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_cli.py +0 -0
  94. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_data_manager.py +0 -0
  95. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_datasources.py +0 -0
  96. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_factors.py +0 -0
  97. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_gm_executors.py +0 -0
  98. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_mixed_datasource.py +0 -0
  99. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_multi_factor.py +0 -0
  100. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_pipeline_integration.py +0 -0
  101. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_symbol_utils.py +0 -0
  102. {quantcli-0.2.2 → quantcli-0.2.5}/tests/test_time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantcli
3
- Version: 0.2.2
3
+ Version: 0.2.5
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quantcli"
7
- version = "0.2.2"
7
+ version = "0.2.5"
8
8
  description = "面向AI的多因子量化选股策略挖掘工具,AI Agent 友好 CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -107,7 +107,7 @@ def data(ctx):
107
107
  @click.option(
108
108
  "--source",
109
109
  type=click.Choice(["akshare", "baostock", "mixed", "mysql"]),
110
- default="mixed",
110
+ default="mysql",
111
111
  help="数据源 (akshare/baostock/mixed/mysql)",
112
112
  )
113
113
  @click.option("--use-cache/--no-cache", default=True, help="是否使用缓存")
@@ -173,7 +173,93 @@ def data_fetch(ctx, symbol, start, end, source, use_cache, cache_raw, output):
173
173
  click.echo(f"Saved to {output}")
174
174
 
175
175
  except Exception as e:
176
- click.echo(f"Error: {e}", err=True)
176
+ error_msg = str(e)
177
+ if "Access denied" in error_msg or "Connection refused" in error_msg:
178
+ click.echo(f"MySQL 连接失败: 请检查数据库配置", err=True)
179
+ click.echo(
180
+ "提示: 使用 --source akshare 或 --source baostock 可以切换到其他数据源",
181
+ err=True,
182
+ )
183
+ else:
184
+ click.echo(f"Error: {e}", err=True)
185
+ sys.exit(1)
186
+
187
+
188
+ @data.command("fetch-fundamental")
189
+ @click.argument("symbols", type=str, nargs=-1)
190
+ @click.option(
191
+ "--date",
192
+ type=date_type,
193
+ default=None,
194
+ help="截止日期 (YYYY-MM-DD,默认今天)",
195
+ )
196
+ @click.option(
197
+ "--source",
198
+ type=click.Choice(["mysql", "baostock"]),
199
+ default="mysql",
200
+ help="数据源 (mysql/baostock)",
201
+ )
202
+ @click.option(
203
+ "--output",
204
+ "-o",
205
+ type=click.Path(),
206
+ default=None,
207
+ help="输出文件路径 (如: data.csv)",
208
+ )
209
+ @click.pass_context
210
+ def data_fetch_fundamental(ctx, symbols, date, source, output):
211
+ """获取股票基本面数据
212
+
213
+ 股票代码:
214
+ 000001 - 平安银行
215
+ 600519 - 贵州茅台
216
+
217
+ 示例:
218
+ quantcli data fetch-fundamental 000001 600519
219
+ quantcli data fetch-fundamental 600519 --date 2024-12-31
220
+ quantcli data fetch-fundamental 000001 --source baostock -o fundamentals.csv
221
+ """
222
+ if not symbols:
223
+ click.echo("Error: At least one symbol is required")
224
+ sys.exit(1)
225
+
226
+ if date is None:
227
+ date = today()
228
+
229
+ click.echo(
230
+ f"Fetching fundamental data for {len(symbols)} symbols at {format_date(date)}..."
231
+ )
232
+
233
+ config = DataConfig(source=source)
234
+ dm = DataManager(config)
235
+
236
+ try:
237
+ df = dm.datasource.get_fundamental(list(symbols), date)
238
+
239
+ if df.empty:
240
+ click.echo(f"No fundamental data found for symbols")
241
+ return
242
+
243
+ click.echo(f"Retrieved {len(df)} rows")
244
+
245
+ click.echo(df.to_string())
246
+
247
+ if output:
248
+ if output.endswith(".csv"):
249
+ df.to_csv(output, index=False)
250
+ elif output.endswith(".parquet"):
251
+ df.to_parquet(output, index=False)
252
+ else:
253
+ df.to_csv(output, index=False)
254
+ click.echo(f"Saved to {output}")
255
+
256
+ except Exception as e:
257
+ error_msg = str(e)
258
+ if "Access denied" in error_msg or "Connection refused" in error_msg:
259
+ click.echo(f"MySQL 连接失败: 请检查数据库配置", err=True)
260
+ click.echo("提示: 使用 --source baostock 可以切换到其他数据源", err=True)
261
+ else:
262
+ click.echo(f"Error: {e}", err=True)
177
263
  sys.exit(1)
178
264
 
179
265
 
@@ -195,7 +281,7 @@ def data_cache(ctx):
195
281
  @click.pass_context
196
282
  def data_cache_ls(ctx, pattern, sort):
197
283
  """列出缓存文件"""
198
- config = DataConfig()
284
+ config = DataConfig(source="mysql")
199
285
  dm = DataManager(config)
200
286
  sizes = dm.get_cache_size()
201
287
 
@@ -233,7 +319,7 @@ def data_cache_ls(ctx, pattern, sort):
233
319
  @click.pass_context
234
320
  def data_cache_clean(ctx, older_than):
235
321
  """清理缓存文件"""
236
- config = DataConfig()
322
+ config = DataConfig(source="mysql")
237
323
  dm = DataManager(config)
238
324
 
239
325
  count = dm.clear_cache(older_than=older_than)
@@ -244,7 +330,7 @@ def data_cache_clean(ctx, older_than):
244
330
  @click.pass_context
245
331
  def data_health(ctx):
246
332
  """检查数据源健康状态"""
247
- config = DataConfig()
333
+ config = DataConfig(source="mysql")
248
334
  dm = DataManager(config)
249
335
  health = dm.health_check()
250
336
 
@@ -309,7 +395,7 @@ def factor_run(ctx, name, expr, symbol, start, end, output):
309
395
  click.echo(f"Computing factor '{name}'...")
310
396
 
311
397
  # 创建引擎
312
- config = DataConfig()
398
+ config = DataConfig(source="mysql")
313
399
  dm = DataManager(config)
314
400
  engine = FactorEngine(dm)
315
401
 
@@ -370,7 +456,7 @@ def factor_eval(ctx, name, symbol, start, end, method):
370
456
 
371
457
  click.echo(f"Evaluating factor '{name}'...")
372
458
 
373
- config = DataConfig()
459
+ config = DataConfig(source="mysql")
374
460
  dm = DataManager(config)
375
461
  engine = FactorEngine(dm)
376
462
 
@@ -735,7 +821,7 @@ def filter_run(
735
821
  if daily_conditions:
736
822
  # 只对候选获取日线数据
737
823
  click.echo(f"Fetching daily data for {len(candidates)} candidates...")
738
- dm = DataManager(DataConfig(source="mixed"))
824
+ dm = DataManager(DataConfig(source="mysql"))
739
825
 
740
826
  price_data = {}
741
827
  for symbol in candidates:
@@ -833,7 +919,7 @@ def filter_run(
833
919
  # 阶段2: 日线
834
920
  click.echo(f"\n=== Stage 2: Daily Screening ===")
835
921
  click.echo(f"Fetching daily data for {len(candidates)} candidates...")
836
- dm = DataManager(DataConfig(source="mixed"))
922
+ dm = DataManager(DataConfig(source="mysql"))
837
923
 
838
924
  price_data = {}
839
925
  for symbol in candidates:
@@ -889,7 +975,7 @@ def filter_run(
889
975
  return
890
976
 
891
977
  click.echo(f"Fetching price data for {len(candidates)} candidates...")
892
- dm = DataManager(DataConfig(source="mixed"))
978
+ dm = DataManager(DataConfig(source="mysql"))
893
979
 
894
980
  price_data = {}
895
981
  for symbol in candidates:
@@ -1307,7 +1393,7 @@ def analyze_ic(ctx, expr, name, symbol, start, end, period, window, method):
1307
1393
  click.echo(f"Forward period: {period} days, Window: {window}")
1308
1394
 
1309
1395
  # 获取数据
1310
- config = DataConfig()
1396
+ config = DataConfig(source="mysql")
1311
1397
  dm = DataManager(config)
1312
1398
  df = dm.get_daily(symbol, start, end)
1313
1399
 
@@ -1426,7 +1512,7 @@ def analyze_batch(ctx, dir, symbol, start, end, period, window, top, output):
1426
1512
  click.echo(f"Analyzing with period={period}d, window={window}...\n")
1427
1513
 
1428
1514
  # 获取数据
1429
- config = DataConfig()
1515
+ config = DataConfig(source="mysql")
1430
1516
  dm = DataManager(config)
1431
1517
  df = dm.get_daily(symbol, start, end)
1432
1518
 
@@ -1545,7 +1631,7 @@ def config():
1545
1631
  @click.pass_context
1546
1632
  def config_show(ctx):
1547
1633
  """显示当前配置"""
1548
- config = DataConfig()
1634
+ config = DataConfig(source="mysql")
1549
1635
  click.echo("QuantCLI Configuration:")
1550
1636
  click.echo(f" data.source: {config.source}")
1551
1637
  click.echo(f" data.cache_dir: {config.cache_dir}")
@@ -88,9 +88,14 @@ class MySQLDataSource(DataSource):
88
88
  self._prefix = config_dict["table_prefix"]
89
89
  self._autocommit = autocommit
90
90
  self._conn = None
91
+ self._tables_initialized = False
91
92
 
92
- # 初始化数据库表
93
+ def _ensure_tables(self):
94
+ """确保数据库表已初始化(懒加载)"""
95
+ if self._tables_initialized:
96
+ return
93
97
  self._init_tables()
98
+ self._tables_initialized = True
94
99
 
95
100
  def _get_connection(self):
96
101
  """获取数据库连接"""
@@ -208,9 +213,8 @@ class MySQLDataSource(DataSource):
208
213
  """批量转换为 MySQL 新格式"""
209
214
  return [to_mysql(s) for s in symbols]
210
215
 
211
- def get_daily(
212
- self, symbol: str, start_date, end_date, fields: Optional[List[str]] = None
213
- ) -> pd.DataFrame:
216
+ def get_daily(self, symbol: str, start_date, end_date) -> pd.DataFrame:
217
+ self._ensure_tables()
214
218
  """获取日线数据
215
219
 
216
220
  Args:
@@ -559,6 +563,7 @@ class MySQLDataSource(DataSource):
559
563
  # ==================== 股票列表和日历 ====================
560
564
 
561
565
  def get_stock_list(self, market: str = "all") -> pd.DataFrame:
566
+ self._ensure_tables()
562
567
  """获取股票列表"""
563
568
  conn = self._get_connection()
564
569
 
@@ -595,6 +600,7 @@ class MySQLDataSource(DataSource):
595
600
  )
596
601
 
597
602
  def get_trading_calendar(self, exchange: str = "SSE") -> List[date]:
603
+ self._ensure_tables()
598
604
  """获取交易日历"""
599
605
  conn = self._get_connection()
600
606
 
@@ -619,6 +625,7 @@ class MySQLDataSource(DataSource):
619
625
  def get_fundamental(
620
626
  self, symbols: List[str], date, indicators: Optional[List[str]] = None
621
627
  ) -> pd.DataFrame:
628
+ self._ensure_tables()
622
629
  """获取基本面数据
623
630
 
624
631
  Args:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantcli
3
- Version: 0.2.2
3
+ Version: 0.2.5
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes