qka 2.2.0__tar.gz → 2.2.1.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 (30) hide show
  1. {qka-2.2.0 → qka-2.2.1.dev2}/PKG-INFO +1 -1
  2. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/backtest.py +9 -1
  3. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/data.py +12 -5
  4. {qka-2.2.0 → qka-2.2.1.dev2}/skills/qka/references/backtest.md +2 -1
  5. {qka-2.2.0 → qka-2.2.1.dev2}/skills/qka/references/data.md +13 -0
  6. {qka-2.2.0 → qka-2.2.1.dev2}/skills/qka/references/strategy.md +17 -0
  7. {qka-2.2.0 → qka-2.2.1.dev2}/.github/workflows/release.yml +0 -0
  8. {qka-2.2.0 → qka-2.2.1.dev2}/.gitignore +0 -0
  9. {qka-2.2.0 → qka-2.2.1.dev2}/.vscode/settings.json +0 -0
  10. {qka-2.2.0 → qka-2.2.1.dev2}/CHANGELOG.md +0 -0
  11. {qka-2.2.0 → qka-2.2.1.dev2}/LICENSE +0 -0
  12. {qka-2.2.0 → qka-2.2.1.dev2}/README.md +0 -0
  13. {qka-2.2.0 → qka-2.2.1.dev2}/pyproject.toml +0 -0
  14. {qka-2.2.0 → qka-2.2.1.dev2}/qka/__init__.py +0 -0
  15. {qka-2.2.0 → qka-2.2.1.dev2}/qka/cli.py +0 -0
  16. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/__init__.py +0 -0
  17. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/accessor.py +0 -0
  18. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/analysis.py +0 -0
  19. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/broker.py +0 -0
  20. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/indicator.py +0 -0
  21. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/report.py +0 -0
  22. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/sizing.py +0 -0
  23. {qka-2.2.0 → qka-2.2.1.dev2}/qka/core/strategy.py +0 -0
  24. {qka-2.2.0 → qka-2.2.1.dev2}/qka/utils/__init__.py +0 -0
  25. {qka-2.2.0 → qka-2.2.1.dev2}/qka/utils/logger.py +0 -0
  26. {qka-2.2.0 → qka-2.2.1.dev2}/skills/qka/SKILL.md +0 -0
  27. {qka-2.2.0 → qka-2.2.1.dev2}/skills/qka/references/analysis.md +0 -0
  28. {qka-2.2.0 → qka-2.2.1.dev2}/skills/qka/references/broker.md +0 -0
  29. {qka-2.2.0 → qka-2.2.1.dev2}/skills/qka/references/sizing.md +0 -0
  30. {qka-2.2.0 → qka-2.2.1.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.2.0
3
+ Version: 2.2.1.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
@@ -79,7 +79,7 @@ class Backtest:
79
79
 
80
80
  def run(self, cash: float = 100000.0,
81
81
  start_date: str = None, end_date: str = None,
82
- benchmark: Optional[str] = None):
82
+ benchmark: Optional[str] = None, warmup: int = 0):
83
83
  """
84
84
  执行回测
85
85
 
@@ -95,6 +95,10 @@ class Backtest:
95
95
  end_date: 回测截止日期 YYYY-MM-DD,None 表示数据最晚日期
96
96
  benchmark: 基准代码,如 '000300.SH'(沪深300)。
97
97
  如果提供,会下载基准数据用于对比。
98
+ warmup: 指标预热天数(默认 0)。自动多读取 warmup 个交易日的历史数据用于
99
+ 计算指标,使策略从第一个交易日起即拿到有效指标值,无需手写
100
+ warmup guard 跳过前 N 根 bar。仅用于计算,on_bar 调用次数不变。
101
+ 显式传入(>0)时覆盖 Data 构造时的 warmup 设定。
98
102
 
99
103
  Returns:
100
104
  None。回测结果保存在 self.results、self.metrics、self.trade_history 中。
@@ -105,6 +109,10 @@ class Backtest:
105
109
  self.strategy.sizing = SizingAccessor(self.strategy.broker)
106
110
  self.strategy._data = DataAccessor()
107
111
 
112
+ # 指标预热:run 显式传入时覆盖 Data 构造时的 warmup 设定
113
+ if warmup > 0:
114
+ self.data.warmup = warmup
115
+
108
116
  # 获取数据
109
117
  raw = self.data.get(lazy=True, start_date=start_date, end_date=end_date)
110
118
 
@@ -74,6 +74,7 @@ class Data():
74
74
  datadir: Optional[Path] = None,
75
75
  indicators: Optional[dict] = None,
76
76
  extra_fields: Optional[List[str]] = None,
77
+ warmup: int = 0,
77
78
  ):
78
79
  """
79
80
  初始化数据对象
@@ -92,7 +93,11 @@ class Data():
92
93
  可选值见 BAOSTOCK_EXTRA_FIELDS。追加的列同样遵循 {symbol}|{field} 命名,
93
94
  如 'sh.600000|peTTM'。注意:首次下载后缓存字段固定,变更 extra_fields
94
95
  会自动检测列缺失并重新下载对应股票。
95
-
96
+
97
+ warmup: 指标预热天数(默认 0)。回测/取数时自动多读取 warmup 个交易日的历史数据
98
+ 用于计算指标,使第 1 个交易日即可拿到有效指标值,无需在策略里手写
99
+ `if len(hist) < N: continue` 跳过前 N 根 bar。仅用于计算,不会增加 on_bar 调用次数。
100
+
96
101
  **1. 字典(混搭 ta 函数和自定义因子):**
97
102
  ```python
98
103
  {
@@ -119,6 +124,7 @@ class Data():
119
124
  self.adjust = adjust
120
125
  self.source = source
121
126
  self.pool_size = pool_size
127
+ self.warmup = warmup
122
128
 
123
129
  # extra_fields 白名单校验 + 去重
124
130
  self.extra_fields = []
@@ -668,12 +674,13 @@ class Data():
668
674
  return df
669
675
 
670
676
  def _min_rows_for_indicators(self):
671
- """计算所有指标所需的最小行数。
677
+ """计算所有指标所需的最小行数(含显式预热天数)。
672
678
 
673
- 遍历所有指标条目的整数参数,取最大值作为窗口上限的保守估计。
679
+ 遍历所有指标条目的整数参数,取最大值作为窗口上限的保守估计,
680
+ 再与显式 warmup 取较大者。
674
681
  """
675
682
  if not self._indicators or not isinstance(self._indicators, dict):
676
- return 0
683
+ return self.warmup
677
684
  max_window = 0
678
685
  for spec in self._indicators.values():
679
686
  if callable(spec) or not isinstance(spec, (list, tuple)):
@@ -686,7 +693,7 @@ class Data():
686
693
  for v in rest[idx:]:
687
694
  if isinstance(v, int):
688
695
  max_window = max(max_window, v)
689
- return max_window
696
+ return max(max_window, self.warmup)
690
697
 
691
698
  def _get_from_baostock(
692
699
  self, symbol: str,
@@ -19,7 +19,7 @@ bt = Backtest(data, strategy)
19
19
 
20
20
  ### run()
21
21
 
22
- `run(cash=100000.0, start_date=None, end_date=None, benchmark=None)`
22
+ `run(cash=100000.0, start_date=None, end_date=None, benchmark=None, warmup=0)`
23
23
 
24
24
  执行回测。注入 broker/sizing/data 后遍历每个交易日,调用 `strategy.on_bar(date)`。
25
25
 
@@ -29,6 +29,7 @@ bt = Backtest(data, strategy)
29
29
  | `start_date` | `None` | 回测起始日期 `'YYYY-MM-DD'` |
30
30
  | `end_date` | `None` | 回测截止日期 |
31
31
  | `benchmark` | `None` | 基准指数代码,baostock 格式如 `'sh.000300'` |
32
+ | `warmup` | `0` | 指标预热天数。自动多读 `warmup` 天历史计算指标,使策略第 1 个 bar 起指标即有效(无需手写 warmup guard)。on_bar 调用次数不变;显式传入(>0)时覆盖 Data 的 warmup 设定 |
32
33
 
33
34
  > 500 bar 以上自动分块迭代,避免一次性加载全量数据。
34
35
 
@@ -26,6 +26,19 @@ data = Data(
26
26
  | `benchmark` | `str` | `None` | 基准指数代码,如 `'sh.000300'`。下载后在 `get()` 结果中追加 `benchmark|returns` 列 |
27
27
  | `indicators` | `dict` | `None` | 预计算指标,见下方 |
28
28
  | `extra_fields` | `list[str]` | `None` | baostock 扩展字段(选股/估值用),见下方 |
29
+ | `warmup` | `int` | `0` | 指标预热天数。回测/取数时自动多读 `warmup` 个交易日历史用于计算指标,使第 1 个交易日即拿到有效指标值(无需手写跳过前 N 根 bar)。仅用于计算,不增加回测 bar 数 |
30
+
31
+ > **warmup(指标预热)**:若指标/因子需要较长历史窗口(如动量排名需看前 200 天),设 `warmup=200`。
32
+ > qka 会在 `[start_date, end_date]` 之前额外多读 `warmup` 天数据用于计算指标,但返回和回测仍严格从
33
+ > `start_date` 起——策略第 1 个 bar 指标即有值,无需 `continue` 跳过前 N 根 bar。
34
+ >
35
+ > ```python
36
+ > data = Data(
37
+ > symbols=['sz.000001'],
38
+ > indicators={'mom200': lambda df: df['close'].pct_change(200)},
39
+ > warmup=200, # 自定义指标 qka 推断不出窗口,显式声明预热天数
40
+ > )
41
+ > ```
29
42
 
30
43
  `get()` 返回的 DataFrame 除了 `open/high/low/close/volume/amount` 六大基本列外,还自动内置一列:
31
44
 
@@ -24,6 +24,7 @@ class MyStrategy(Strategy):
24
24
  | `__init__` | **必须**调用 `super().__init__()`。不需要传任何参数 |
25
25
  | `on_bar` 签名 | 只有 `self` 和 `date`。**没有** `get` 参数(旧版 API,已废弃) |
26
26
  | `self.params` | **不存在**。不要写 `self.params.get('fast', 5)`,直接用实例属性 |
27
+ | 预热 guard | **不要**手写 `if len(hist) < N: continue` 跳过前 N 根 bar。改用 `Data(warmup=N)` 或 `bt.run(warmup=N)`,qka 自动多读历史计算指标 |
27
28
 
28
29
  `Backtest.run()` 在执行时注入以下属性:
29
30
 
@@ -75,6 +76,22 @@ hist = self.history('close', 20) # 最近 20 天的收盘价
75
76
  | 列 | 股票代码 |
76
77
  | 异常 | 因子不存在时返回空 DataFrame(有索引无列),不抛异常 |
77
78
 
79
+ ### 指标预热(warmup)
80
+
81
+ 若策略用到的指标/因子需要较长历史窗口(如动量排名需看前 200 天数据),**不要**在 `on_bar` 里
82
+ 手写 `if len(hist) < N: continue` 跳过前 N 根 bar。改用 `Data(warmup=N)` 或 `bt.run(warmup=N)`,
83
+ qka 自动在 `[start_date, end_date]` 之前多读 `warmup` 天数据用于计算指标,但回测仍从 `start_date`
84
+ 首个交易日开始(`on_bar` 调用次数不变),指标自始有效:
85
+
86
+ ```python
87
+ data = Data(symbols=[...], indicators={...}, warmup=200) # 构造时声明
88
+ # 或
89
+ bt.run(warmup=200) # 回测时声明(覆盖 Data 的设定)
90
+ ```
91
+
92
+ > 注意:qka 只能从 ta 指标的整数参数自动推断预热窗口;自定义 lambda 指标推断不到,
93
+ > 必须显式用 `warmup` 声明窗口天数,否则开头一段仍为 `NaN`。
94
+
78
95
  ### self.broker / self.sizing
79
96
 
80
97
  由 `Backtest.run()` 注入(见上方注入表),通过 `self.broker.buy()` / `self.broker.sell()` 和 `self.sizing.percent()` 等方法使用。详见 [Broker](#broker) 和 [SizingAccessor](#sizingaccessor) 独立章节。
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