qka 2.5.7.dev2__tar.gz → 2.5.8__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 (42) hide show
  1. {qka-2.5.7.dev2 → qka-2.5.8}/PKG-INFO +1 -1
  2. qka-2.5.8/qka/core/data/__init__.py +12 -0
  3. qka-2.5.8/qka/core/data/_common.py +130 -0
  4. qka-2.5.8/qka/core/data/base.py +940 -0
  5. qka-2.5.8/qka/core/data/cache.py +335 -0
  6. qka-2.5.8/qka/core/data/factors.py +988 -0
  7. qka-2.5.8/qka/core/data/sources/__init__.py +1 -0
  8. qka-2.5.8/qka/core/data/sources/baostock.py +95 -0
  9. qka-2.5.8/qka/core/data/sources/sim.py +169 -0
  10. qka-2.5.8/qka/core/data/sources/tdx.py +114 -0
  11. qka-2.5.8/qka/core/data/sources/tickflow.py +236 -0
  12. qka-2.5.7.dev2/qka/core/data.py +0 -2677
  13. {qka-2.5.7.dev2 → qka-2.5.8}/.github/workflows/release.yml +0 -0
  14. {qka-2.5.7.dev2 → qka-2.5.8}/.gitignore +0 -0
  15. {qka-2.5.7.dev2 → qka-2.5.8}/.vscode/settings.json +0 -0
  16. {qka-2.5.7.dev2 → qka-2.5.8}/CHANGELOG.md +0 -0
  17. {qka-2.5.7.dev2 → qka-2.5.8}/LICENSE +0 -0
  18. {qka-2.5.7.dev2 → qka-2.5.8}/README.md +0 -0
  19. {qka-2.5.7.dev2 → qka-2.5.8}/pyproject.toml +0 -0
  20. {qka-2.5.7.dev2 → qka-2.5.8}/qka/__init__.py +0 -0
  21. {qka-2.5.7.dev2 → qka-2.5.8}/qka/cli.py +0 -0
  22. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/__init__.py +0 -0
  23. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/accessor.py +0 -0
  24. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/analysis.py +0 -0
  25. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/backtest.py +0 -0
  26. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/broker.py +0 -0
  27. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/indicator.py +0 -0
  28. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/report.py +0 -0
  29. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/simulate.py +0 -0
  30. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/sizing.py +0 -0
  31. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/strategy.py +0 -0
  32. {qka-2.5.7.dev2 → qka-2.5.8}/qka/core/tdx.py +0 -0
  33. {qka-2.5.7.dev2 → qka-2.5.8}/qka/utils/__init__.py +0 -0
  34. {qka-2.5.7.dev2 → qka-2.5.8}/qka/utils/logger.py +0 -0
  35. {qka-2.5.7.dev2 → qka-2.5.8}/skills/qka/SKILL.md +0 -0
  36. {qka-2.5.7.dev2 → qka-2.5.8}/skills/qka/references/analysis.md +0 -0
  37. {qka-2.5.7.dev2 → qka-2.5.8}/skills/qka/references/backtest.md +0 -0
  38. {qka-2.5.7.dev2 → qka-2.5.8}/skills/qka/references/broker.md +0 -0
  39. {qka-2.5.7.dev2 → qka-2.5.8}/skills/qka/references/data.md +0 -0
  40. {qka-2.5.7.dev2 → qka-2.5.8}/skills/qka/references/sizing.md +0 -0
  41. {qka-2.5.7.dev2 → qka-2.5.8}/skills/qka/references/strategy.md +0 -0
  42. {qka-2.5.7.dev2 → qka-2.5.8}/tools/generate_api_ref.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: qka
3
- Version: 2.5.7.dev2
3
+ Version: 2.5.8
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
@@ -0,0 +1,12 @@
1
+ """qka 数据模块(2026-11-13 按职责拆分,对外接口保持不变)。
2
+
3
+ 原来是一个 2702 行的 data.py,取数 / 缓存 / 因子 / 三个数据源 / 模拟数据
4
+ 全混在一个类里。现在按职责分到 base + cache + factors + sources/*,
5
+ `from qka.core.data import Data` 这类 import 一行都不用改。
6
+
7
+ 注意:data.py 文件已删除,本包取代它 —— Python 的 import 路径完全一致。
8
+ """
9
+ from ._common import (get_tickflow_api_key, set_tickflow_api_key)
10
+ from .base import Data
11
+
12
+ __all__ = ['Data', 'set_tickflow_api_key', 'get_tickflow_api_key']
@@ -0,0 +1,130 @@
1
+ """
2
+ QKA数据模块
3
+
4
+ 提供统一的数据获取、缓存和管理功能,支持多数据源、多周期、多因子的数据获取。
5
+ """
6
+
7
+ import contextlib
8
+ import hashlib
9
+ import inspect
10
+ import io
11
+ import json
12
+ import os
13
+ import textwrap
14
+ import threading
15
+ from pathlib import Path
16
+ from tqdm import tqdm
17
+ from concurrent.futures import ThreadPoolExecutor, as_completed
18
+ import pandas as pd
19
+ import numpy as np
20
+ import pyarrow as pa
21
+ import pyarrow.parquet as pq
22
+ import baostock as bs
23
+ import dask.dataframe as dd
24
+ from typing import List, Optional, Callable, Union
25
+ from qka.utils.logger import logger
26
+ from qka.core import indicator
27
+ from qka.core.simulate import Simulate
28
+ from qka.core import tdx
29
+
30
+ # qka 内置指标裸名集合,用于 dispatch 识别(无需 qka. 前缀)
31
+ _QKA_BUILTIN_NAMES = frozenset({
32
+ 'alpha', 'beta', 'sharpe', 'max_drawdown',
33
+ 'information_ratio', 'rolling_zigzag',
34
+ })
35
+
36
+
37
+ def _is_qka_factor(ind_type):
38
+ """判断指标类型是否为 qka 内置指标(支持 qka. 前缀或裸名)。"""
39
+ return (isinstance(ind_type, str) and
40
+ (ind_type.startswith('qka.') or ind_type in _QKA_BUILTIN_NAMES))
41
+
42
+
43
+ def _callable_source(fn):
44
+ """取可调用对象的源码文本,用于算配方指纹;取不到返回 None(表示不缓存)。"""
45
+ try:
46
+ return textwrap.dedent(inspect.getsource(fn))
47
+ except (OSError, TypeError):
48
+ return None
49
+
50
+
51
+ _RF_SIG_CACHE = {}
52
+
53
+
54
+ def _accepts_rf(fn) -> bool:
55
+ """该指标是否接受 `rf` 参数(α / 夏普接受,其余不接受)。
56
+
57
+ 签名解析只做一次并缓存 —— 否则每只票每个因子都要解析一遍,几千次纯浪费。
58
+ """
59
+ key = (getattr(fn, '__module__', ''), getattr(fn, '__name__', ''), id(fn))
60
+ hit = _RF_SIG_CACHE.get(key)
61
+ if hit is None:
62
+ try:
63
+ hit = 'rf' in inspect.signature(fn).parameters
64
+ except (TypeError, ValueError):
65
+ hit = False
66
+ _RF_SIG_CACHE[key] = hit
67
+ return hit
68
+
69
+
70
+ def _rf_given_positionally(fn, args) -> bool:
71
+ """因子定义里是否已经按位置把 rf 填掉了,如 `('qka.alpha', 250, 0.02)`。
72
+
73
+ 这种写法要继续有效,并且**优先于** Data 的全局 risk_free ——
74
+ 否则两边一起给会报 `got multiple values for argument 'rf'`。
75
+ """
76
+ try:
77
+ sig = inspect.signature(fn)
78
+ # 前两个位置参数固定是 df / symbol
79
+ bound = sig.bind_partial(object(), object(), *args)
80
+ except (TypeError, ValueError):
81
+ return False
82
+ return 'rf' in bound.arguments
83
+
84
+
85
+ # ── TickFlow 凭据(进程级,设一次即可)──────────────────────────
86
+ # 放在模块级而不是 `Data` 参数里:「用哪个凭据」是部署级配置,不该每构造一次
87
+ # Data 就传一遍。优先级:set_tickflow_api_key() > 环境变量 > 免费档。
88
+ _TICKFLOW_API_KEY: Optional[str] = None
89
+
90
+
91
+ def set_tickflow_api_key(api_key: Optional[str]) -> None:
92
+ """设置 TickFlow API key(进程级,设一次即可)。
93
+
94
+ 设了之后所有 `Data(source='tickflow')` 都走完整服务
95
+ (https://api.tickflow.org);不设则走免费档
96
+ (https://free-api.tickflow.org),与既有行为完全一致。
97
+
98
+ 优先级:本函数设的值 > 环境变量 ``TICKFLOW_API_KEY`` > 免费档。
99
+
100
+ Args:
101
+ api_key: 密钥;传 None 或空串表示清除,回到免费档。
102
+
103
+ Examples:
104
+ >>> from qka import Data, set_tickflow_api_key
105
+ >>> set_tickflow_api_key('tk_xxx')
106
+ >>> Data(symbols=['600519.SH']).get() # 已走完整服务
107
+ """
108
+ global _TICKFLOW_API_KEY
109
+ _TICKFLOW_API_KEY = api_key or None
110
+ # 已经建好的客户端按旧 key 建的,作废掉 —— 否则「先建了免费档客户端、
111
+ # 后设 key」这条时间线会静默继续走免费档,既看不出来又难查。
112
+ from .base import Data as _D
113
+ _D._reset_tickflow_clients()
114
+
115
+
116
+ def get_tickflow_api_key() -> Optional[str]:
117
+ """当前生效的 TickFlow key:函数设的优先,其次环境变量,都没有则 None。"""
118
+ return _TICKFLOW_API_KEY or os.environ.get('TICKFLOW_API_KEY') or None
119
+
120
+
121
+ def _key_fingerprint(api_key: Optional[str]) -> str:
122
+ """key 的短摘要,用作客户端缓存的键。
123
+
124
+ 不拿明文 key 当字典键 —— 免得它出现在内存转储或日志里。
125
+ """
126
+ if not api_key:
127
+ return ''
128
+ return hashlib.sha1(api_key.encode('utf-8')).hexdigest()[:12]
129
+
130
+