qka 2.4.2.dev2__py3-none-any.whl → 2.4.3.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/__init__.py CHANGED
@@ -13,7 +13,7 @@ except PackageNotFoundError:
13
13
 
14
14
  # 核心功能直接导入
15
15
  from qka.core.simulate import Simulate
16
- from qka.core.data import Data
16
+ from qka.core.data import Data, set_tickflow_api_key
17
17
  from qka.core.accessor import DataAccessor
18
18
  from qka.core.backtest import Backtest
19
19
  from qka.core.strategy import Strategy
@@ -33,6 +33,8 @@ __all__ = [
33
33
  'Analysis', 'Segment', 'AlphaBeta',
34
34
  # 通达信数据源
35
35
  'set_tdx_root', 'get_tdx_root_info',
36
+ # TickFlow 凭据(进程级设一次)
37
+ 'set_tickflow_api_key',
36
38
  # 子模块
37
39
  'core', 'utils'
38
40
  ]
qka/core/data.py CHANGED
@@ -48,6 +48,51 @@ def _callable_source(fn):
48
48
  return None
49
49
 
50
50
 
51
+ # ── TickFlow 凭据(进程级,设一次即可)──────────────────────────
52
+ # 放在模块级而不是 `Data` 参数里:「用哪个凭据」是部署级配置,不该每构造一次
53
+ # Data 就传一遍。优先级:set_tickflow_api_key() > 环境变量 > 免费档。
54
+ _TICKFLOW_API_KEY: Optional[str] = None
55
+
56
+
57
+ def set_tickflow_api_key(api_key: Optional[str]) -> None:
58
+ """设置 TickFlow API key(进程级,设一次即可)。
59
+
60
+ 设了之后所有 `Data(source='tickflow')` 都走完整服务
61
+ (https://api.tickflow.org);不设则走免费档
62
+ (https://free-api.tickflow.org),与既有行为完全一致。
63
+
64
+ 优先级:本函数设的值 > 环境变量 ``TICKFLOW_API_KEY`` > 免费档。
65
+
66
+ Args:
67
+ api_key: 密钥;传 None 或空串表示清除,回到免费档。
68
+
69
+ Examples:
70
+ >>> from qka import Data, set_tickflow_api_key
71
+ >>> set_tickflow_api_key('tk_xxx')
72
+ >>> Data(symbols=['600519.SH']).get() # 已走完整服务
73
+ """
74
+ global _TICKFLOW_API_KEY
75
+ _TICKFLOW_API_KEY = api_key or None
76
+ # 已经建好的客户端按旧 key 建的,作废掉 —— 否则「先建了免费档客户端、
77
+ # 后设 key」这条时间线会静默继续走免费档,既看不出来又难查。
78
+ Data._reset_tickflow_clients()
79
+
80
+
81
+ def get_tickflow_api_key() -> Optional[str]:
82
+ """当前生效的 TickFlow key:函数设的优先,其次环境变量,都没有则 None。"""
83
+ return _TICKFLOW_API_KEY or os.environ.get('TICKFLOW_API_KEY') or None
84
+
85
+
86
+ def _key_fingerprint(api_key: Optional[str]) -> str:
87
+ """key 的短摘要,用作客户端缓存的键。
88
+
89
+ 不拿明文 key 当字典键 —— 免得它出现在内存转储或日志里。
90
+ """
91
+ if not api_key:
92
+ return ''
93
+ return hashlib.sha1(api_key.encode('utf-8')).hexdigest()[:12]
94
+
95
+
51
96
  # 当日日线数据「可能已经生成」的时刻(本地时间)。一天以它为界切成前后两个时段:
52
97
  # 17:00 之前,数据源手里最新的一根还是上一交易日,当天这根根本还没生成;
53
98
  # 17:00 之后,当天的已经出完了,要补也就这一次。
@@ -1662,24 +1707,44 @@ class Data():
1662
1707
 
1663
1708
  # ── TickFlow 数据源 ────────────────────────────────────────
1664
1709
 
1665
- _tf_client = None # 进程级复用,避免重复初始化
1710
+ _tf_clients = {} # {key 摘要: 客户端} —— 进程级复用;按 key 分开存,不同 key 不互相串
1666
1711
  _tf_client_lock = threading.Lock()
1667
1712
 
1713
+ @classmethod
1714
+ def _reset_tickflow_clients(cls):
1715
+ """丢掉已建好的 tickflow 客户端,下次按当前 key 重建。
1716
+
1717
+ 由 `set_tickflow_api_key()` 调用:客户端一旦建好就缓存住,不清的话
1718
+ 「先建了免费档客户端、后设 key」会静默继续走免费档。
1719
+ """
1720
+ with cls._tf_client_lock:
1721
+ cls._tf_clients.clear()
1722
+
1668
1723
  @classmethod
1669
1724
  def _tickflow_client(cls):
1670
- """惰性创建并复用 TickFlow 客户端(当前走免费服务,无需 api_key)。"""
1671
- if cls._tf_client is None:
1725
+ """惰性创建并复用 TickFlow 客户端。
1726
+
1727
+ 有 key 走完整服务(api.tickflow.org),没有就走免费档
1728
+ (free-api.tickflow.org)。key 是**建客户端这一刻**现取的,
1729
+ 所以设 key 的时机不必早于 import(见 `set_tickflow_api_key`)。
1730
+ """
1731
+ key = get_tickflow_api_key()
1732
+ cache_key = _key_fingerprint(key)
1733
+ client = cls._tf_clients.get(cache_key)
1734
+ if client is None:
1672
1735
  with cls._tf_client_lock:
1673
- if cls._tf_client is None:
1736
+ client = cls._tf_clients.get(cache_key)
1737
+ if client is None:
1674
1738
  from tickflow import TickFlow
1675
- # TickFlow.free() 会往 stdout 打一段带 emoji 的「免费档」说明。
1739
+ # free() 会往 stdout 打一段带 emoji 的「免费档」说明。
1676
1740
  # stdout 被重定向到文件、且编码非 UTF-8 时(如打包版把后端输出
1677
1741
  # 写入 GBK 日志),print emoji 会抛 UnicodeEncodeError 而中断下载,
1678
1742
  # 故创建期间丢弃其标准输出。(redirect_stdout 改的是全局 sys.stdout,
1679
1743
  # 并发首次创建会互相覆盖,故用锁串行化。)
1680
1744
  with contextlib.redirect_stdout(io.StringIO()):
1681
- cls._tf_client = TickFlow.free()
1682
- return cls._tf_client
1745
+ client = TickFlow(api_key=key) if key else TickFlow.free()
1746
+ cls._tf_clients[cache_key] = client
1747
+ return client
1683
1748
 
1684
1749
  @staticmethod
1685
1750
  def _split_symbol(symbol: str):
@@ -106,6 +106,42 @@ datadir/factors/{因子名}/{symbol}.parquet # 单列,日期索引
106
106
 
107
107
  **作用范围**:只作用于默认(`lazy=False`)路径;`lazy=True`(回测走的那条)行为不变。不传 `factor_cache` 时,一切跟没有这个功能一样:不读、不写、不建目录。
108
108
 
109
+ ### TickFlow 凭据
110
+
111
+ TickFlow 有两个入口,qka 按「有没有 key」自动选:
112
+
113
+ | 有没有 key | 走的入口 | 域名 |
114
+ |---|---|---|
115
+ | 有 | 完整服务 | `https://api.tickflow.org` |
116
+ | 没有 | 免费档 | `https://free-api.tickflow.org` |
117
+
118
+ 不设 key 时与旧版行为完全一致(免费档提供日线、标的元信息、交易所、池子查询、复权因子;实时行情与分钟线是完整服务的能力)。
119
+
120
+ 设 key 的方式是**进程级、设一次即可** —— 不用每次构造 `Data` 都传:
121
+
122
+ ```python
123
+ from qka import Data, set_tickflow_api_key
124
+
125
+ set_tickflow_api_key('tk_xxx') # 设一次,之后所有 source='tickflow' 的取数都生效
126
+ data = Data(symbols=['600519.SH']) # 不用再传任何东西
127
+ data.get()
128
+
129
+ set_tickflow_api_key(None) # 清除,回到免费档
130
+ ```
131
+
132
+ 优先级:**`set_tickflow_api_key()` > 环境变量 `TICKFLOW_API_KEY` > 免费档**(显式盖过隐式)。
133
+
134
+ 不想改代码的话,部署时设环境变量即可:
135
+
136
+ ```bash
137
+ export TICKFLOW_API_KEY=tk_xxx
138
+ ```
139
+
140
+ 两点实现细节,用的时候心里有数:
141
+
142
+ - 读 key 的时机是**建客户端那一刻**,不是 `import` 时 —— 所以设 key 不必早于 `import qka`。
143
+ - 换 key(或从免费档切到 key)时,已建好的客户端会**自动作废重建**,不会继续沿用旧入口。
144
+
109
145
  ### 通达信数据源
110
146
 
111
147
  `source='tdx'` 走本机安装的「支持 TQ 策略」通达信客户端(在进程内 `import tqcenter`,由客户端与本机 `TdxW.exe` 通信)。前提与边界:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: qka
3
- Version: 2.4.2.dev2
3
+ Version: 2.4.3.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
@@ -1,11 +1,11 @@
1
- qka/__init__.py,sha256=2EgIGy3W2NFvxniqHKpCYRhRIOSGRrkEa94gr3UEezo,1087
1
+ qka/__init__.py,sha256=xFkw3C-0TzeNkr5hPtz-0ZWwLao3cihUAUCXSE8FO1c,1183
2
2
  qka/cli.py,sha256=12ZkbSZmE7hSmz5_O67gOXVKROIdymCQevnNfEOApd8,466
3
3
  qka/core/__init__.py,sha256=kVhvzSA915MXOk9axq9T-COlv3OeTPfv2avX58WxElE,578
4
4
  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=kzMAFbyGFvGE--Qypiot6wAp0jTiEsOPbaPd8Zs3o1M,13541
7
7
  qka/core/broker.py,sha256=FxeHh9qZ9M6FNFLd8EXwztA4kmC9zZzkrXin2hdVFa4,9662
8
- qka/core/data.py,sha256=KjjE7ltK7R5Yyxc63BQiPwwK5hQhg_yBVbn3CEUieXo,86796
8
+ qka/core/data.py,sha256=j5QNO_LbgU84mxC5rSIhyeaB-bL9HgTZUy5AelBYnVY,89607
9
9
  qka/core/indicator.py,sha256=OId8bgYFCzDgjPr27rb6zHYxkPYX0XrjlWylGSQ2KCU,6447
10
10
  qka/core/report.py,sha256=TaZoxWk7ZIPLhA9Ss9UOmxWY2ymzKJRUnxSzmyPcBTI,22386
11
11
  qka/core/simulate.py,sha256=bdThckUFj0Y3GyAj5ahLIGv3HetJFTbyxbEcX2SUKn0,2915
@@ -18,11 +18,11 @@ qka/skills/qka/SKILL.md,sha256=lddd_JvGZ0x4JBlz_IB1cd4ScHRZ30-IC9SWI9Tjbvw,2132
18
18
  qka/skills/qka/references/analysis.md,sha256=GdFbK3fa8sFgvcxJ1xzkToF4QwsCFXf_EgKb9bbRgwo,3587
19
19
  qka/skills/qka/references/backtest.md,sha256=ZXL58zpuYHAJglPNk52an4fP3i0Ggnpuf0pg0QlbPk4,4803
20
20
  qka/skills/qka/references/broker.md,sha256=wr62v1asRM-wBdsADIsB406eXm_ENYcrwGMANQobaa0,1083
21
- qka/skills/qka/references/data.md,sha256=dspj4yqK0oZP5vFGAdfQRvGmk4RQupAr4L4DsByUQ-I,27472
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.4.2.dev2.dist-info/METADATA,sha256=AXXFmn0jkeEh0KLSljcNeCjMVpHYnJrt3haeozdWUzE,7545
25
- qka-2.4.2.dev2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
26
- qka-2.4.2.dev2.dist-info/entry_points.txt,sha256=quG9zg-rOKKtbg-6FiXkoxkUUTMltMBD0hZ0QUnJ6RM,37
27
- qka-2.4.2.dev2.dist-info/licenses/LICENSE,sha256=3iIG35SnJNF11yIzm3vxa2QUvy9SS5rktM3O1DL2vqM,1075
28
- qka-2.4.2.dev2.dist-info/RECORD,,
24
+ qka-2.4.3.dev2.dist-info/METADATA,sha256=bOyyD3-Bjjlh5EzzCUtfEWKg69KNpvzgWiGTBhMadto,7545
25
+ qka-2.4.3.dev2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
26
+ qka-2.4.3.dev2.dist-info/entry_points.txt,sha256=quG9zg-rOKKtbg-6FiXkoxkUUTMltMBD0hZ0QUnJ6RM,37
27
+ qka-2.4.3.dev2.dist-info/licenses/LICENSE,sha256=3iIG35SnJNF11yIzm3vxa2QUvy9SS5rktM3O1DL2vqM,1075
28
+ qka-2.4.3.dev2.dist-info/RECORD,,