Skip to content

Commit a7122bd

Browse files
Merge pull request #185 from akfamily/dev
docs: 补充并行优化日志回传与严格参数校验的文档与测试
2 parents 5a661c0 + 0674b0d commit a7122bd

12 files changed

Lines changed: 415 additions & 35 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "akquant"
3-
version = "0.1.94"
3+
version = "0.1.95"
44
edition = "2024"
55
description = "High-performance quantitative trading framework based on Rust and Python"
66
license = "MIT"

docs/en/guide/optimization.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ results = run_grid_search(
142142
* `result_filter`: (Optional) Callback function to filter results based on metrics.
143143
* `warmup_calc`: (Optional) Callback function for dynamic warmup period calculation.
144144
* `constraint`: (Optional) Callback function for parameter constraints to filter invalid combinations.
145+
* `forward_worker_logs`: (Optional) Whether to forward worker-process `self.log()` output to the main process during parallel optimization.
146+
* `False` (default): Better throughput, worker logs may not be visible in main process output.
147+
* `True`: Main process aggregates worker logs, useful for debugging and teaching.
148+
* `strict_strategy_params`: (default `True`, injected by `run_grid_search` into `run_backtest`).
149+
* Enforces strict validation between `param_grid` keys and strategy constructor signature.
150+
* Raises fast on unknown parameters to avoid silent fallback and distorted optimization results.
145151

146152
### Resource Control & Error Handling
147153

@@ -187,6 +193,14 @@ if __name__ == "__main__":
187193
main()
188194
```
189195

196+
### Parallel Log Visibility & Warning Rules
197+
198+
When `max_workers > 1`, warning behavior is tied to `forward_worker_logs`:
199+
200+
* `forward_worker_logs=False`: warns that worker logs may not be visible in the main process.
201+
* `forward_worker_logs=True` with active main-process logger handlers: log forwarding is enabled and visibility warning is suppressed.
202+
* `forward_worker_logs=True` without active main-process handlers: warns that forwarding was requested but no handler is available.
203+
190204
### Persistence & Resume
191205

192206
For scenarios with extremely large parameter sets (e.g., > 10,000 combinations), running on a single machine might take days. AKQuant supports real-time result persistence to SQLite, enabling breakpoint resume.
@@ -245,7 +259,10 @@ wfo_results = run_walk_forward(
245259
metric="sharpe_ratio", # Optimization target
246260
initial_cash=100_000.0,
247261
warmup_calc=warmup_calc, # Support dynamic warmup
248-
constraint=param_constraint # Support parameter constraints
262+
constraint=param_constraint, # Support parameter constraints
263+
max_workers=4,
264+
forward_worker_logs=True, # Forward in-sample worker logs
265+
strict_strategy_params=True, # Keep strict constructor validation
249266
)
250267

251268
# wfo_results contains the concatenated equity curve and parameters used for each segment
@@ -257,6 +274,10 @@ print(wfo_results)
257274
* `train_period`: Training window length (number of Bars). Longer windows mean more stable parameters; shorter windows adapt faster to changes.
258275
* `test_period`: Test window length (number of Bars). Usually also the rolling step size.
259276
* `metric`: The metric used to select optimal parameters on the training set (e.g., `sharpe_ratio`, `total_return`).
277+
* `kwargs` passthrough rules:
278+
* forwarded to `run_grid_search` during in-sample optimization;
279+
* forwarded to `run_backtest` during out-of-sample validation;
280+
* therefore `forward_worker_logs` and `strict_strategy_params` remain available in WFO.
260281

261282
---
262283

docs/en/reference/api.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def run_backtest(
6060
broker_profile: Optional[str] = None,
6161
timer_execution_policy: Literal["same_cycle", "next_event"] = "same_cycle",
6262
fill_policy: Optional[Dict[str, str]] = None,
63+
strict_strategy_params: bool = True,
6364
**kwargs: Any,
6465
) -> BacktestResult
6566
```
@@ -82,6 +83,9 @@ def run_backtest(
8283
* `price_basis`: `next_open`, `current_close`, `ohlc4` (OHLC average), or `hl2` (high-low midpoint).
8384
* Reserved (not implemented yet): `mid_quote`, `vwap_window`, `twap_window` (currently raises `NotImplementedError`).
8485
* `temporal`: `same_cycle` or `next_event`.
86+
* `strict_strategy_params`: Whether to strictly validate strategy constructor parameters (default `True`).
87+
* Raises immediately if unsupported constructor parameters are provided.
88+
* Recommended to keep enabled to avoid silent parameter mismatch and distorted backtest results.
8589
* `t_plus_one`: Enable T+1 trading rule (Default False). If enabled, it forces usage of China Market Model.
8690
* `slippage`: Global slippage (Default 0.0). E.g., 0.0001 means 1bp (0.01%) slippage, using percent model.
8791
* `volume_limit_pct`: Volume limit percentage (Default 0.25). Limits single trade to not exceed this percentage of the bar's total volume.
@@ -112,6 +116,70 @@ def run_backtest(
112116
| `execution_mode="next_average"` | `fill_policy={"price_basis":"ohlc4","temporal":"same_cycle"}` |
113117
| `execution_mode="next_high_low_mid"` | `fill_policy={"price_basis":"hl2","temporal":"same_cycle"}` |
114118

119+
### `akquant.run_grid_search`
120+
121+
Grid-search entry for batch backtesting and metric-based parameter ranking.
122+
123+
```python
124+
def run_grid_search(
125+
strategy: Type[Strategy],
126+
param_grid: Dict[str, Sequence[Any]],
127+
data: Union[pd.DataFrame, Dict[str, pd.DataFrame], List[Bar]],
128+
sort_by: Union[str, List[str]] = "sharpe_ratio",
129+
ascending: Union[bool, List[bool]] = False,
130+
return_df: bool = True,
131+
result_filter: Optional[Callable[[Dict[str, Any]], bool]] = None,
132+
constraint: Optional[Callable[[Dict[str, Any]], bool]] = None,
133+
max_workers: Optional[int] = None,
134+
show_progress: bool = True,
135+
timeout: Optional[float] = None,
136+
max_tasks_per_child: Optional[int] = None,
137+
db_path: Optional[str] = None,
138+
forward_worker_logs: bool = False,
139+
**kwargs: Any,
140+
) -> Union[pd.DataFrame, List[OptimizationResult]]
141+
```
142+
143+
**Key parameter notes:**
144+
145+
* `forward_worker_logs`: Whether to forward worker-process strategy logs to the main process during parallel optimization.
146+
* `False`: throughput-first; worker logs may be invisible in main-process output.
147+
* `True`: enables log aggregation for debugging.
148+
* `strict_strategy_params`: Passed via `**kwargs` into `run_backtest` (defaulted to `True` inside `run_grid_search`).
149+
* Enforces strict match between `param_grid` keys and strategy constructor parameters.
150+
* Fails fast on mismatch to avoid silent fallback.
151+
152+
### `akquant.run_walk_forward`
153+
154+
Walk-forward entry. Executes rolling "in-sample optimization + out-of-sample validation" and concatenates OOS equity curves.
155+
156+
```python
157+
def run_walk_forward(
158+
strategy: Type[Strategy],
159+
param_grid: Mapping[str, Sequence[Any]],
160+
data: pd.DataFrame,
161+
train_period: int,
162+
test_period: int,
163+
metric: Union[str, List[str]] = "sharpe_ratio",
164+
ascending: Union[bool, List[bool]] = False,
165+
initial_cash: float = 100_000.0,
166+
warmup_period: int = 0,
167+
warmup_calc: Optional[Any] = None,
168+
constraint: Optional[Any] = None,
169+
result_filter: Optional[Any] = None,
170+
compounding: bool = False,
171+
timeout: Optional[float] = None,
172+
max_tasks_per_child: Optional[int] = None,
173+
**kwargs: Any,
174+
) -> pd.DataFrame
175+
```
176+
177+
**Key parameter notes:**
178+
179+
* `**kwargs` are forwarded to both `run_grid_search` (in-sample optimization) and `run_backtest` (out-of-sample validation).
180+
* Therefore, `forward_worker_logs` controls worker-log forwarding during in-sample parallel optimization.
181+
* `strict_strategy_params` stays effective across optimization and validation phases (strict by default).
182+
115183
### `akquant.run_warm_start`
116184

117185
Resume a backtest from snapshot state and continue execution.

docs/en/textbook/11_optimization.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,33 @@ This chapter is currently maintained in Chinese first.
99
- Extended example: [examples/02_parameter_optimization.py](https://github.com/akfamily/akquant/blob/main/examples/02_parameter_optimization.py)
1010
- Guide: [Optimization Guide](../guide/optimization.md)
1111

12+
Key optimization parameters (new):
13+
14+
- `forward_worker_logs` in `run_grid_search`:
15+
- `False` (default): better throughput; worker `self.log()` may not be visible in main process output.
16+
- `True`: forwards worker logs to the main process for debugging and teaching.
17+
- `strict_strategy_params` in `run_backtest` (default `True`):
18+
- enforces strict constructor parameter validation;
19+
- fails fast on unknown strategy parameters to avoid silent fallback and misleading optimization results.
20+
- `run_walk_forward` accepts these options via `**kwargs` passthrough:
21+
- `forward_worker_logs` applies to in-sample optimization (`run_grid_search`);
22+
- `strict_strategy_params` stays effective in both optimization and OOS validation.
23+
24+
WFO passthrough example:
25+
26+
```python
27+
wfo_results = run_walk_forward(
28+
strategy=TailTradingStrategy,
29+
param_grid=param_grid,
30+
data=all_data,
31+
train_period=250,
32+
test_period=60,
33+
max_workers=4,
34+
forward_worker_logs=True,
35+
strict_strategy_params=True,
36+
)
37+
```
38+
1239
Windows note for parallel optimization (`max_workers > 1`):
1340

1441
- Define strategy classes in an importable module, not in `__main__`.

docs/zh/guide/optimization.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ results = run_grid_search(
142142
* `result_filter`: (可选) 结果筛选回调函数,用于过滤不符合条件的组合。
143143
* `warmup_calc`: (可选) 动态计算预热期的回调函数。
144144
* `constraint`: (可选) 参数约束回调函数,用于过滤无效组合。
145+
* `forward_worker_logs`: (可选) 并行优化时是否将子进程 `self.log()` 回传到主进程日志。
146+
* `False` (默认): 性能更优,日志可能在主进程不可见。
147+
* `True`: 主进程可聚合子进程日志,适合排障与教学演示。
148+
* `strict_strategy_params`: (默认 `True`,由 `run_grid_search` 注入到 `run_backtest`)。
149+
* 启用后会严格校验 `param_grid` 是否与策略构造函数参数匹配;
150+
* 发现未知参数时立即抛错,避免“静默回退”导致优化结果失真。
145151

146152
### 资源控制与异常处理 (Resource Control & Error Handling)
147153

@@ -187,6 +193,14 @@ if __name__ == "__main__":
187193
main()
188194
```
189195

196+
### 并行日志可见性与提示规则
197+
198+
`max_workers > 1` 场景下,日志提示与 `forward_worker_logs` 的关系如下:
199+
200+
* `forward_worker_logs=False`:会提示“子进程日志可能在主进程不可见”。
201+
* `forward_worker_logs=True` 且主进程存在有效 logger handler:启用日志回传,不再显示“不可见”提示。
202+
* `forward_worker_logs=True` 但主进程无有效 logger handler:会提示“请求了日志回传但主进程无可用 handler”。
203+
190204
### 持久化与断点续传 (Persistence & Resume)
191205

192206
对于参数组合极多(如 > 10,000 组)的场景,单机运行可能需要数小时甚至数天。如果中途断电或程序崩溃,重新运行将非常耗时。AKQuant 支持将优化结果实时写入 SQLite 数据库,并支持断点续传。
@@ -245,7 +259,10 @@ wfo_results = run_walk_forward(
245259
metric="sharpe_ratio", # 优化目标
246260
initial_cash=100_000.0,
247261
warmup_calc=warmup_calc, # 支持动态预热
248-
constraint=param_constraint # 支持参数约束
262+
constraint=param_constraint, # 支持参数约束
263+
max_workers=4,
264+
forward_worker_logs=True, # 样本内并行优化日志回传
265+
strict_strategy_params=True, # 严格参数校验(推荐保持开启)
249266
)
250267

251268
# wfo_results 包含拼接后的资金曲线和每段使用的参数
@@ -259,6 +276,10 @@ print(wfo_results)
259276
* `metric`: 在训练集上选择最优参数的依据指标。支持单个字符串 (如 `"sharpe_ratio"`) 或字符串列表 (如 `["sharpe_ratio", "total_return"]`)。
260277
* `ascending`: 排序方向,与 `metric` 对应。支持布尔值或布尔值列表 (默认 `False`,即降序)。
261278
* `result_filter`: (可选) 结果筛选回调函数,在每个训练窗口中过滤不符合条件的参数组合。
279+
* `kwargs` 透传规则:
280+
* 样本内优化阶段透传给 `run_grid_search`
281+
* 样本外验证阶段透传给 `run_backtest`
282+
* 因此可在 WFO 中继续使用 `forward_worker_logs``strict_strategy_params`
262283

263284
---
264285

docs/zh/reference/api.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,75 @@ def run_backtest(
4747
broker_profile: Optional[str] = None,
4848
timer_execution_policy: Literal["same_cycle", "next_event"] = "same_cycle",
4949
fill_policy: Optional[Dict[str, str]] = None,
50+
strict_strategy_params: bool = True,
5051
**kwargs: Any,
5152
) -> BacktestResult
5253
```
5354

55+
### `akquant.run_grid_search`
56+
57+
参数网格搜索入口,用于批量回测并按指标排序返回最优参数组合。
58+
59+
```python
60+
def run_grid_search(
61+
strategy: Type[Strategy],
62+
param_grid: Dict[str, Sequence[Any]],
63+
data: Union[pd.DataFrame, Dict[str, pd.DataFrame], List[Bar]],
64+
sort_by: Union[str, List[str]] = "sharpe_ratio",
65+
ascending: Union[bool, List[bool]] = False,
66+
return_df: bool = True,
67+
result_filter: Optional[Callable[[Dict[str, Any]], bool]] = None,
68+
constraint: Optional[Callable[[Dict[str, Any]], bool]] = None,
69+
max_workers: Optional[int] = None,
70+
show_progress: bool = True,
71+
timeout: Optional[float] = None,
72+
max_tasks_per_child: Optional[int] = None,
73+
db_path: Optional[str] = None,
74+
forward_worker_logs: bool = False,
75+
**kwargs: Any,
76+
) -> Union[pd.DataFrame, List[OptimizationResult]]
77+
```
78+
79+
**关键参数补充:**
80+
81+
* `forward_worker_logs`: 并行优化时是否将子进程策略日志回传到主进程。
82+
* `False`:吞吐优先,日志可能在主进程不可见。
83+
* `True`:启用日志聚合,便于排障。
84+
* `strict_strategy_params`: 通过 `**kwargs` 传递给 `run_backtest`(默认在 `run_grid_search` 内为 `True`)。
85+
* 严格校验 `param_grid` 与策略构造参数匹配关系;
86+
* 参数不匹配时快速失败,避免静默回退。
87+
88+
### `akquant.run_walk_forward`
89+
90+
滚动优化入口。按窗口执行“样本内参数优化 + 样本外验证”,并拼接样本外资金曲线。
91+
92+
```python
93+
def run_walk_forward(
94+
strategy: Type[Strategy],
95+
param_grid: Mapping[str, Sequence[Any]],
96+
data: pd.DataFrame,
97+
train_period: int,
98+
test_period: int,
99+
metric: Union[str, List[str]] = "sharpe_ratio",
100+
ascending: Union[bool, List[bool]] = False,
101+
initial_cash: float = 100_000.0,
102+
warmup_period: int = 0,
103+
warmup_calc: Optional[Any] = None,
104+
constraint: Optional[Any] = None,
105+
result_filter: Optional[Any] = None,
106+
compounding: bool = False,
107+
timeout: Optional[float] = None,
108+
max_tasks_per_child: Optional[int] = None,
109+
**kwargs: Any,
110+
) -> pd.DataFrame
111+
```
112+
113+
**关键参数补充:**
114+
115+
* `**kwargs` 会透传到 `run_grid_search`(样本内优化阶段)与 `run_backtest`(样本外验证阶段)。
116+
* 因此,`forward_worker_logs` 可用于控制样本内并行优化日志回传。
117+
* 同时,`strict_strategy_params` 会在优化与回测阶段保持严格参数校验语义(默认严格)。
118+
54119
### `akquant.run_warm_start`
55120

56121
从快照恢复并继续运行回测(支持多策略 slot 执行)。
@@ -102,6 +167,9 @@ def run_warm_start(
102167
* `price_basis`: `next_open``current_close``ohlc4`OHLC 平均价)或 `hl2`(高低中价)。
103168
* 预留(暂未实现): `mid_quote``vwap_window``twap_window`(当前会抛出 `NotImplementedError`)。
104169
* `temporal`: `same_cycle``next_event`
170+
* `strict_strategy_params`: 是否严格校验策略构造参数(默认 `True`)。
171+
* 当传入策略不接受的参数时会立即抛错;
172+
* 推荐保持默认值,避免参数错配被静默忽略导致回测结果偏差。
105173
* `t_plus_one`: 是否启用 T+1 交易规则 (默认 False)。如果启用,将强制使用中国市场模型。
106174
* `slippage`: 全局滑点 (默认 0.0)。例如 0.0001 代表 1bp (0.01%) 的滑点,采用百分比模型。
107175
* `volume_limit_pct`: 成交量限制比例 (默认 0.25)。限制单笔成交不超过该 Bar 总成交量的百分比。

docs/zh/textbook/11_optimization.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,47 @@ class SmaStrategy(Strategy):
157157
--8<-- "examples/textbook/ch11_optimization.py"
158158
```
159159

160+
### 11.4.2A 新参数(并行日志与严格参数校验)
161+
162+
为提升优化可观测性与结果可靠性,推荐关注以下参数:
163+
164+
* `forward_worker_logs``run_grid_search`):
165+
* `False`(默认):性能优先,子进程日志可能在主进程不可见;
166+
* `True`:将子进程 `self.log()` 聚合回主进程,适合排障与教学演示。
167+
* `strict_strategy_params``run_backtest`,默认 `True`):
168+
* 严格校验策略构造参数;
169+
*`param_grid` 中存在策略不接受的参数时,立即抛错,避免静默回退导致“看似跑完但结果无效”。
170+
* `run_walk_forward` 也支持通过 `**kwargs` 透传这两个参数:
171+
* `forward_worker_logs` 作用于样本内优化阶段(内部 `run_grid_search`);
172+
* `strict_strategy_params` 在样本内优化与样本外验证阶段都生效。
173+
174+
示例:
175+
176+
```python
177+
results = run_grid_search(
178+
strategy=TailTradingStrategy,
179+
param_grid=param_grid,
180+
data=all_data,
181+
max_workers=4,
182+
forward_worker_logs=True,
183+
)
184+
```
185+
186+
WFO 传导示例:
187+
188+
```python
189+
wfo_results = run_walk_forward(
190+
strategy=TailTradingStrategy,
191+
param_grid=param_grid,
192+
data=all_data,
193+
train_period=250,
194+
test_period=60,
195+
max_workers=4,
196+
forward_worker_logs=True,
197+
strict_strategy_params=True,
198+
)
199+
```
200+
160201
### 11.4.3 结果分析
161202

162203
运行上述代码后,我们会得到一个按夏普比率排序的参数表。

docs/zh/textbook/index.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,6 @@
7575
* 因子表达式的原理与优势
7676
* Polars 高性能计算架构
7777
* 案例:Alpha101 因子实战 ([examples/textbook/ch14_factor.py](https://github.com/akfamily/akquant/blob/main/examples/textbook/ch14_factor.py))
78-
* **[第 16 章:AKQuant 指标全景与工程化使用](16_rust_indicators.md)**
79-
* AKQuant 支持的 103 个指标分类、解释与 warmup 口径
80-
* 指标词典与教学脚手架联动(见 [AKQuant 指标全量说明](../guide/rust_indicator_reference.md)
81-
8278
### 第五部分:从回测到实盘 (Live Trading)
8379

8480
* **[第 15 章:实盘交易系统与运维](15_live_trading.md)**
@@ -87,6 +83,12 @@
8783
* 进阶示例:动态策略加载与运行时注入 ([examples/textbook/ch15_strategy_loader.py](https://github.com/akfamily/akquant/blob/main/examples/textbook/ch15_strategy_loader.py))
8884
* 风控与熔断机制
8985

86+
### 第六部分:指标工程与工具链 (Indicator Engineering)
87+
88+
* **[第 16 章:AKQuant 指标全景与工程化使用](16_rust_indicators.md)**
89+
* AKQuant 支持的 103 个指标分类、解释与 warmup 口径
90+
* 指标词典与教学脚手架联动(见 [AKQuant 指标全量说明](../guide/rust_indicator_reference.md)
91+
9092
## 章节示例映射(主示例 / 进阶示例 / 对应指南)
9193

9294
| 章节 | 主示例 | 进阶示例 | 对应指南 |

0 commit comments

Comments
 (0)