qka 2.4.3.dev2__tar.gz → 2.4.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.
- {qka-2.4.3.dev2 → qka-2.4.4}/PKG-INFO +1 -1
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/data.py +68 -1
- {qka-2.4.3.dev2 → qka-2.4.4}/.github/workflows/release.yml +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/.gitignore +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/.vscode/settings.json +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/CHANGELOG.md +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/LICENSE +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/README.md +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/pyproject.toml +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/__init__.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/cli.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/__init__.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/accessor.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/analysis.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/backtest.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/broker.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/indicator.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/report.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/simulate.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/sizing.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/strategy.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/core/tdx.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/utils/__init__.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/qka/utils/logger.py +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/skills/qka/SKILL.md +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/skills/qka/references/analysis.md +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/skills/qka/references/backtest.md +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/skills/qka/references/broker.md +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/skills/qka/references/data.md +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/skills/qka/references/sizing.md +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/skills/qka/references/strategy.md +0 -0
- {qka-2.4.3.dev2 → qka-2.4.4}/tools/generate_api_ref.py +0 -0
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|