qka 2.4.3.dev2__tar.gz → 2.4.4.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 (32) hide show
  1. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/PKG-INFO +1 -1
  2. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/data.py +68 -1
  3. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/.github/workflows/release.yml +0 -0
  4. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/.gitignore +0 -0
  5. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/.vscode/settings.json +0 -0
  6. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/CHANGELOG.md +0 -0
  7. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/LICENSE +0 -0
  8. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/README.md +0 -0
  9. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/pyproject.toml +0 -0
  10. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/__init__.py +0 -0
  11. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/cli.py +0 -0
  12. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/__init__.py +0 -0
  13. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/accessor.py +0 -0
  14. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/analysis.py +0 -0
  15. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/backtest.py +0 -0
  16. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/broker.py +0 -0
  17. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/indicator.py +0 -0
  18. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/report.py +0 -0
  19. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/simulate.py +0 -0
  20. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/sizing.py +0 -0
  21. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/strategy.py +0 -0
  22. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/core/tdx.py +0 -0
  23. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/utils/__init__.py +0 -0
  24. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/qka/utils/logger.py +0 -0
  25. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/skills/qka/SKILL.md +0 -0
  26. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/skills/qka/references/analysis.md +0 -0
  27. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/skills/qka/references/backtest.md +0 -0
  28. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/skills/qka/references/broker.md +0 -0
  29. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/skills/qka/references/data.md +0 -0
  30. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/skills/qka/references/sizing.md +0 -0
  31. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/skills/qka/references/strategy.md +0 -0
  32. {qka-2.4.3.dev2 → qka-2.4.4.dev2}/tools/generate_api_ref.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: qka
3
- Version: 2.4.3.dev2
3
+ Version: 2.4.4.dev2
4
4
  Summary: QKA(快量化 / Quant Kit for A-shares)- 简洁易用的 A 股量化回测框架
5
5
  Project-URL: Home, https://github.com/zsrl/qka
6
6
  Project-URL: Repository, https://github.com/zsrl/qka
@@ -197,6 +197,7 @@ class Data():
197
197
  indicators: Optional[dict] = None,
198
198
  extra_fields: Optional[List[str]] = None,
199
199
  warmup: int = 0,
200
+ drop_head: int = 0,
200
201
  factor_cache: bool = False,
201
202
  fill_suspended: bool = False,
202
203
  ):
@@ -269,6 +270,15 @@ class Data():
269
270
  用于计算因子,使第 1 个交易日即可拿到有效值,无需在策略里手写
270
271
  `if len(hist) < N: continue` 跳过前 N 根 bar。仅用于计算,不会增加 on_bar 调用次数。
271
272
 
273
+ drop_head: 掐头根数(默认 0,即不掐)。取回数据的**每条序列**(含基准)
274
+ 的前 drop_head 根,`returns` 一律记空 —— 这些根只当"垫场",不参与
275
+ 任何因子的滚动窗口。为什么需要:次新股的头一两根 K 线是「发行价跳到
276
+ 上市价」,那笔涨幅不是市场交易出来的(见过一天"涨 349%"的假数据),
277
+ 掐掉之后,窗口自动需要 drop_head 根额外的真实样本 —— 上市不足
278
+ 「窗口 + drop_head」根的票,因子整条留空,不会拿脏数据凑数。
279
+ 与 warmup 的分工:warmup 管「往前多取多少料」,drop_head 管
280
+ 「取回来的料开头多少根不许用」。两者互不替代,可同时使用。
281
+
272
282
  factor_cache: 是否把算出来的因子**纵向落盘**(默认 False,即不缓存)。
273
283
  开启后,因子按 `datadir/factors/{因子名}/{symbol}.parquet` 存,
274
284
  命中就直接读、只缺尾巴就只补尾巴;配方或数据口径变了则整条重算。
@@ -312,6 +322,9 @@ class Data():
312
322
  self.source = source
313
323
  self.pool_size = pool_size
314
324
  self.warmup = warmup
325
+ # 掐头根数:取回数据的每条序列开头 N 根,returns 记空(见 __init__ 文档)。
326
+ # 必须在停牌补齐**之后**生效 —— 补齐会重算 returns,先掐会被洗回来。
327
+ self.drop_head = int(drop_head)
315
328
  # 因子缓存:默认关。要用得显式开(见 _factors_with_cache)
316
329
  self.factor_cache = bool(factor_cache)
317
330
  # 停牌日按前收填充:默认关。见 _fill_suspended_prices
@@ -938,6 +951,7 @@ class Data():
938
951
  continue
939
952
  ddf = dd.read_parquet(str(parquet_path), filters=pq_filters)
940
953
  ddf['returns'] = ddf['close'].diff() / ddf['close'].shift(1)
954
+ ddf = self._drop_head_lazy(ddf)
941
955
  ddf = self._apply_factors(ddf)
942
956
  column_mapping = {col: f'{symbol}|{col}' for col in ddf.columns}
943
957
  dfs.append(ddf.rename(columns=column_mapping))
@@ -994,6 +1008,10 @@ class Data():
994
1008
  # 否则因子看到的还是带空缺的序列,长窗口照样算不出来。
995
1009
  result = self._fill_suspended_prices(result)
996
1010
 
1011
+ # 掐头(默认关,见 _drop_head_returns)。必须在补齐之后、算因子之前:
1012
+ # 补齐会重算 returns,先掐会被洗回来;因子在后面才吃 returns。
1013
+ result = self._drop_head_returns(result)
1014
+
997
1015
  # 因子:逐票逐因子贴上(带缓存 —— 全盖住就不算,只缺尾巴就只补尾巴)
998
1016
  # 注意在切片之前算:此时还带着预热数据,滚动窗口才够长。
999
1017
  result = self._factors_with_cache(result)
@@ -1020,6 +1038,13 @@ class Data():
1020
1038
  return df
1021
1039
  bench_df = pd.read_parquet(bench_path, filters=pq_filters)
1022
1040
  bench_df['returns'] = bench_df['close'].diff() / bench_df['close'].shift(1)
1041
+ # 基准同样掐头:规则对所有序列一视同仁("来啥废啥",不猜哪天上市)
1042
+ if self.drop_head > 0:
1043
+ if len(bench_df) > self.drop_head:
1044
+ cutoff = bench_df.index[self.drop_head - 1]
1045
+ bench_df['returns'] = bench_df['returns'].where(bench_df.index > cutoff)
1046
+ else:
1047
+ bench_df['returns'] = np.nan
1023
1048
  bench_returns = bench_df['returns'].rename('benchmark|returns')
1024
1049
 
1025
1050
  if isinstance(df, dd.DataFrame):
@@ -1029,6 +1054,47 @@ class Data():
1029
1054
  df['benchmark|returns'] = bench_returns.reindex(df.index)
1030
1055
  return df
1031
1056
 
1057
+ def _drop_head_returns(self, result):
1058
+ """掐头(默认路径用):每条序列的前 drop_head 根,returns 记空。
1059
+
1060
+ 语义是「来啥废啥」—— 不猜哪天上市、不做探测:取回来的数据,开头 N 根
1061
+ 一律当垫场。调用方把 start 传在上市日,N 根垫场就正好是「上市后前 N 个
1062
+ 交易日」;start 传得更晚,也只是白掐 20 根(少 20 个样本),数值永远是对的。
1063
+
1064
+ 为什么放在停牌补齐之后:补齐会按补好的 close 重算 returns,先掐会被洗回来。
1065
+
1066
+ 定位"一只票自己的前 N 根":按 close 非空计数(宽表里上市前的行 close 是
1067
+ 空的,不算数);基准列没有 close,退而按 returns 自身的非空值计数。
1068
+ """
1069
+ if self.drop_head <= 0:
1070
+ return result
1071
+ for col in [c for c in result.columns if c.endswith('|returns')]:
1072
+ base = col[:-len('|returns')]
1073
+ close_col = result[f'{base}|close'] if f'{base}|close' in result.columns else None
1074
+ counter = close_col if close_col is not None else result[col]
1075
+ head = counter.notna().cumsum() <= self.drop_head
1076
+ result.loc[head, col] = np.nan
1077
+ return result
1078
+
1079
+ def _drop_head_lazy(self, ddf):
1080
+ """掐头(懒加载路径用):单票自己的序列,前 drop_head 根 returns 记空。
1081
+
1082
+ 懒加载时每只票还是独立的 dask 帧(尚未拼宽表),帧的开头就是它自己的头。
1083
+ 用「第 N 个索引标签」做切点:比它晚的行保留,早的(含第 N 根)记空。
1084
+ """
1085
+ if self.drop_head <= 0:
1086
+ return ddf
1087
+ labels = ddf.index.to_series().head(self.drop_head).compute()
1088
+ if labels.empty:
1089
+ return ddf
1090
+ if len(labels) < self.drop_head:
1091
+ # 序列总共不到 N 根:整条记空
1092
+ return ddf.map_partitions(lambda part: part.assign(returns=np.nan))
1093
+ cutoff = labels.iloc[-1]
1094
+ return ddf.map_partitions(
1095
+ lambda part: part.assign(returns=part['returns'].where(part.index > cutoff))
1096
+ )
1097
+
1032
1098
  def _apply_factors(self, df):
1033
1099
  """
1034
1100
  对单只股票的数据应用预定义的指标/因子。
@@ -1321,7 +1387,7 @@ class Data():
1321
1387
  def _factor_signature(self, name, spec):
1322
1388
  """单个因子的配方指纹(定义 + 数据口径);认不出返回 None。
1323
1389
 
1324
- 数据口径 = 数据源 / 周期 / 复权 / **停牌是否补齐** —— 任一样变了,
1390
+ 数据口径 = 数据源 / 周期 / 复权 / **停牌是否补齐** / **掐头根数** —— 任一样变了,
1325
1391
  算出来的值就不一样,指纹必须跟着变,否则缓存会拿旧口径的值冒充新值。
1326
1392
  """
1327
1393
  payload = self._spec_payload(spec)
@@ -1335,6 +1401,7 @@ class Data():
1335
1401
  'period': self.period,
1336
1402
  'adjust': self.adjust,
1337
1403
  'fill_suspended': self.fill_suspended,
1404
+ 'drop_head': self.drop_head,
1338
1405
  }, sort_keys=True, ensure_ascii=False)
1339
1406
  return hashlib.sha1(blob.encode('utf-8')).hexdigest()
1340
1407
 
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