qka 2.5.1.dev3__py3-none-any.whl → 2.5.2.dev2__py3-none-any.whl
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/core/data.py +76 -19
- {qka-2.5.1.dev3.dist-info → qka-2.5.2.dev2.dist-info}/METADATA +1 -1
- {qka-2.5.1.dev3.dist-info → qka-2.5.2.dev2.dist-info}/RECORD +6 -6
- {qka-2.5.1.dev3.dist-info → qka-2.5.2.dev2.dist-info}/WHEEL +0 -0
- {qka-2.5.1.dev3.dist-info → qka-2.5.2.dev2.dist-info}/entry_points.txt +0 -0
- {qka-2.5.1.dev3.dist-info → qka-2.5.2.dev2.dist-info}/licenses/LICENSE +0 -0
qka/core/data.py
CHANGED
|
@@ -1211,9 +1211,17 @@ class Data():
|
|
|
1211
1211
|
only: 只算这几个列名(None = 全算)。因子缓存按因子粒度补算时用。
|
|
1212
1212
|
"""
|
|
1213
1213
|
|
|
1214
|
-
# 分区过小时跳过,避免 ta-lib 在 dask meta
|
|
1215
|
-
|
|
1216
|
-
|
|
1214
|
+
# 分区过小时跳过,避免 ta-lib 在 dask meta 推断时崩溃。
|
|
1215
|
+
# 只补尾巴时这张表必然比「全局最长窗口」短(10 年期 2500、1 年期只要 250),
|
|
1216
|
+
# 门槛得按**本次要算的那几个因子**自己的窗口来定,否则 1 年期因子会被
|
|
1217
|
+
# 2500 行的全局门槛整个拦掉、算出一片空白(2026-10-21 改)。
|
|
1218
|
+
need = 0
|
|
1219
|
+
if only:
|
|
1220
|
+
for _n in only:
|
|
1221
|
+
need = max(need, self._spec_window(self._factors.get(_n)))
|
|
1222
|
+
if need <= 0:
|
|
1223
|
+
need = self._min_rows_for_factors()
|
|
1224
|
+
if len(df) < need:
|
|
1217
1225
|
return df
|
|
1218
1226
|
|
|
1219
1227
|
for col_name, spec in self._factors.items():
|
|
@@ -1711,29 +1719,70 @@ class Data():
|
|
|
1711
1719
|
pq.write_table(table.replace_schema_metadata(meta), path)
|
|
1712
1720
|
self._quote_cache[symbol] = (out, new_metas)
|
|
1713
1721
|
|
|
1714
|
-
def _recompute_target(self, full, cached):
|
|
1722
|
+
def _recompute_target(self, full, cached, window=None, probe_col=None):
|
|
1715
1723
|
"""算出「这次要重算哪一段」。
|
|
1716
1724
|
|
|
1717
1725
|
- 没有缓存 → 整段算(返回 full)
|
|
1718
1726
|
- 缓存全盖住 → 不用算(返回 None)
|
|
1719
|
-
- 只缺尾巴 →
|
|
1727
|
+
- 只缺尾巴 → **只算尾巴**(往前只留该因子自己那一个窗口当预热)
|
|
1720
1728
|
- 前面也缺 → 整段算
|
|
1729
|
+
|
|
1730
|
+
⚠️ 预热只留「一个窗口」,不是两倍(2026-10-21 改):算第 t 天的值,滚动
|
|
1731
|
+
窗口最多用到 t-window+1 那天的数据,而调用方只把缓存末日之后的行写回,
|
|
1732
|
+
多留的窗口纯属白算。之前固定按「全局最长窗口 × 2」= 5000 个工作日回溯,
|
|
1733
|
+
等于把 2007 年以来的因子全部重算一遍,连只需 250 天的 1 年期因子也被拖着走。
|
|
1734
|
+
|
|
1735
|
+
⚠️ 预热按**交易日位置**回溯,不按工作日推算:`BDay(2500)` 只覆盖约 1712 个
|
|
1736
|
+
交易日(节假日不算工作日),长窗口因子会预热不足、尾部一串 NaN。
|
|
1721
1737
|
"""
|
|
1722
1738
|
if cached is None:
|
|
1723
1739
|
return full
|
|
1724
1740
|
if cached.index.min() <= full.index.min() and cached.index.max() >= full.index.max():
|
|
1725
1741
|
return None
|
|
1726
1742
|
if cached.index.min() > full.index.min():
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1743
|
+
# 组合宽表的索引是「所有标的日期的并集」:该票上市之前那些行,本来
|
|
1744
|
+
# 就没有它的数据,缓存从上市日起就是完整的 —— 不能因此判成「前面缺、
|
|
1745
|
+
# 整段重算」(2026-10-21 改)。之前这里只比 index.min(),导致除全场
|
|
1746
|
+
# 最早上市那只票以外,**每一只票每次都被判成整段重算**,增量形同虚设。
|
|
1747
|
+
# 真正的「缓存残缺」要看:该票明明有数据,缓存却没盖住那段。
|
|
1748
|
+
probe = probe_col if (probe_col and probe_col in full.columns) else None
|
|
1749
|
+
if probe is None:
|
|
1750
|
+
return full
|
|
1751
|
+
head = full.loc[full.index < cached.index.min(), probe]
|
|
1752
|
+
if head.notna().any():
|
|
1753
|
+
return full
|
|
1754
|
+
win = window if isinstance(window, int) and window > 0 else 0
|
|
1755
|
+
if win <= 0:
|
|
1756
|
+
win = self._min_rows_for_factors()
|
|
1757
|
+
if win <= 0:
|
|
1730
1758
|
# 窗口完全靠猜(lambda + 没声明 warmup)⇒ 不敢只补尾巴,整段重算
|
|
1731
1759
|
return full
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1760
|
+
# cached 末日在这张表里的行号;它之后的每一行才是真正要新算的
|
|
1761
|
+
pos = int(full.index.searchsorted(cached.index.max(), side='right')) - 1
|
|
1762
|
+
start = pos - win
|
|
1763
|
+
if pos < 0 or start <= 0:
|
|
1764
|
+
# 历史还不够一个窗口(或缓存末日对不上这张表),谈不上「只补尾巴」
|
|
1735
1765
|
return full
|
|
1736
|
-
return
|
|
1766
|
+
return full.iloc[start:].copy()
|
|
1767
|
+
|
|
1768
|
+
@staticmethod
|
|
1769
|
+
def _spec_window(spec) -> int:
|
|
1770
|
+
"""从因子定义里取窗口长度(最大的整数参数);取不到返回 0。
|
|
1771
|
+
|
|
1772
|
+
口径与 `_min_rows_for_factors` 一致:跳过前导字符串(那些是列名),
|
|
1773
|
+
剩下的整数里取最大。`('qka.alpha', 250, {'max_abs_return': 0.5})` → 250。
|
|
1774
|
+
"""
|
|
1775
|
+
if not isinstance(spec, (list, tuple)):
|
|
1776
|
+
return 0
|
|
1777
|
+
rest = list(spec[1:])
|
|
1778
|
+
idx = 0
|
|
1779
|
+
while idx < len(rest) and isinstance(rest[idx], str):
|
|
1780
|
+
idx += 1
|
|
1781
|
+
win = 0
|
|
1782
|
+
for v in rest[idx:]:
|
|
1783
|
+
if isinstance(v, int) and v > win:
|
|
1784
|
+
win = v
|
|
1785
|
+
return win
|
|
1737
1786
|
|
|
1738
1787
|
@staticmethod
|
|
1739
1788
|
def _columns_by_symbol(columns):
|
|
@@ -1869,7 +1918,9 @@ class Data():
|
|
|
1869
1918
|
for name in names:
|
|
1870
1919
|
spec, fp = plan[name]
|
|
1871
1920
|
cached = self._read_factor_cache(name, symbol, fp)
|
|
1872
|
-
target = self._recompute_target(
|
|
1921
|
+
target = self._recompute_target(
|
|
1922
|
+
sub, cached, self._spec_window(spec),
|
|
1923
|
+
probe_col='close' if 'close' in sub.columns else None)
|
|
1873
1924
|
if target is None:
|
|
1874
1925
|
sub[name] = cached.reindex(sub.index)
|
|
1875
1926
|
continue
|
|
@@ -1877,9 +1928,11 @@ class Data():
|
|
|
1877
1928
|
if target is sub:
|
|
1878
1929
|
series = sub[name]
|
|
1879
1930
|
else:
|
|
1880
|
-
#
|
|
1881
|
-
#
|
|
1882
|
-
|
|
1931
|
+
# 只把缓存末日**之后**的行写回,历史值原样沿用缓存:后复权历史
|
|
1932
|
+
# 不会被将来的分红改写,早于缓存末日的因子值不可能变,重算它们
|
|
1933
|
+
# 纯属白烧 CPU;尾巴头部那些预热行同样不该覆盖老值。
|
|
1934
|
+
fresh = target[name][target.index > cached.index.max()]
|
|
1935
|
+
series = fresh.reindex(sub.index).combine_first(
|
|
1883
1936
|
cached.reindex(sub.index)
|
|
1884
1937
|
)
|
|
1885
1938
|
sub[name] = series
|
|
@@ -1933,7 +1986,10 @@ class Data():
|
|
|
1933
1986
|
collected[col] = view[col]
|
|
1934
1987
|
continue
|
|
1935
1988
|
cached = self._read_factor_cache(name, symbol, fp)
|
|
1936
|
-
target = self._recompute_target(
|
|
1989
|
+
target = self._recompute_target(
|
|
1990
|
+
view, cached, self._spec_window(spec),
|
|
1991
|
+
probe_col=(f'{symbol}|close' if f'{symbol}|close' in view.columns
|
|
1992
|
+
else None))
|
|
1937
1993
|
if target is None:
|
|
1938
1994
|
collected[col] = cached.reindex(result.index)
|
|
1939
1995
|
continue
|
|
@@ -1942,8 +1998,9 @@ class Data():
|
|
|
1942
1998
|
if target is view:
|
|
1943
1999
|
series = view[col]
|
|
1944
2000
|
else:
|
|
1945
|
-
#
|
|
1946
|
-
|
|
2001
|
+
# 同上:只写回缓存末日之后的行,历史值沿用缓存
|
|
2002
|
+
fresh = target[col][target.index > cached.index.max()]
|
|
2003
|
+
series = fresh.reindex(result.index).combine_first(
|
|
1947
2004
|
cached.reindex(result.index)
|
|
1948
2005
|
)
|
|
1949
2006
|
collected[col] = series
|
|
@@ -5,7 +5,7 @@ qka/core/accessor.py,sha256=iEZrteVe8nqv3s5Va2l3Vz50N2S6X0IOElKq9Ik3Wno,2893
|
|
|
5
5
|
qka/core/analysis.py,sha256=EXCO4qhzd2av5Yb4jihcNji1V-VFPOceUHKtg-q8bLs,11142
|
|
6
6
|
qka/core/backtest.py,sha256=SEbx1fFj7Poda9hGTtc2YoK-Enlxa83bauRzHVidk8I,13958
|
|
7
7
|
qka/core/broker.py,sha256=FxeHh9qZ9M6FNFLd8EXwztA4kmC9zZzkrXin2hdVFa4,9662
|
|
8
|
-
qka/core/data.py,sha256=
|
|
8
|
+
qka/core/data.py,sha256=Z19mVoz9Qx18hjGEaUVQjx8W_YHjEd6UO4PaeCQ96aM,109854
|
|
9
9
|
qka/core/indicator.py,sha256=WYzfM7pMrzC45A7EA3ygGdHah-NlAAGFBcPxzUkrtko,9355
|
|
10
10
|
qka/core/report.py,sha256=AhlLgT6xTd9YjW7fF1T6PBCXSYEtP8GTSvrW-A4p_kg,22481
|
|
11
11
|
qka/core/simulate.py,sha256=bdThckUFj0Y3GyAj5ahLIGv3HetJFTbyxbEcX2SUKn0,2915
|
|
@@ -21,8 +21,8 @@ qka/skills/qka/references/broker.md,sha256=wr62v1asRM-wBdsADIsB406eXm_ENYcrwGMAN
|
|
|
21
21
|
qka/skills/qka/references/data.md,sha256=6mL44eGOk_xXLqQ62Wmv1DFYcXn3kayYWYWMd8-hFOk,28857
|
|
22
22
|
qka/skills/qka/references/sizing.md,sha256=3SsX2ddQoHjE177dberW0Q7wYxW-M_2DG_-sV3CCo9A,656
|
|
23
23
|
qka/skills/qka/references/strategy.md,sha256=-6xa_oigTTj77Siity5bTC5MbdF3UKfld9imnevlAAc,4455
|
|
24
|
-
qka-2.5.
|
|
25
|
-
qka-2.5.
|
|
26
|
-
qka-2.5.
|
|
27
|
-
qka-2.5.
|
|
28
|
-
qka-2.5.
|
|
24
|
+
qka-2.5.2.dev2.dist-info/METADATA,sha256=mTYIcHuaAl9AU44CypExW1y0Y_846R1cY87gG37gFoE,7545
|
|
25
|
+
qka-2.5.2.dev2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
26
|
+
qka-2.5.2.dev2.dist-info/entry_points.txt,sha256=quG9zg-rOKKtbg-6FiXkoxkUUTMltMBD0hZ0QUnJ6RM,37
|
|
27
|
+
qka-2.5.2.dev2.dist-info/licenses/LICENSE,sha256=3iIG35SnJNF11yIzm3vxa2QUvy9SS5rktM3O1DL2vqM,1075
|
|
28
|
+
qka-2.5.2.dev2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|