"""参数空间定义与生成。 用于定义策略参数的搜索空间,支持: - range:整数范围 - list:离散值列表 - 可扩展:连续分布、对数空间等 示例: param_space = { "short_window": range(3, 21, 2), "long_window": range(20, 61, 5), "max_hold_days": range(3, 11), "stop_loss_pct": [0.03, 0.05, 0.08], "take_profit_pct": [0.10, 0.15, 0.20], } combinations = generate_param_combinations(param_space) """ from __future__ import annotations import itertools from typing import Any, Dict, List, Union from utils.logger import setup_logger logger = setup_logger(__name__) def generate_param_combinations(param_space: Dict[str, Union[range, List]]) -> List[Dict[str, Any]]: """根据参数空间生成所有参数组合(笛卡尔积)。 参数: param_space: 参数名 -> 取值范围/列表的字典 返回: List[Dict]: 参数组合列表,每个元素是一组参数 示例: >>> param_space = {"a": range(1, 3), "b": [0.1, 0.2]} >>> generate_param_combinations(param_space) [ {"a": 1, "b": 0.1}, {"a": 1, "b": 0.2}, {"a": 2, "b": 0.1}, {"a": 2, "b": 0.2} ] """ if not param_space: return [{}] # 提取参数名和值列表 param_names = list(param_space.keys()) param_values = [] for param_name in param_names: values = param_space[param_name] # 如果是 range 对象,转为列表 if isinstance(values, range): param_values.append(list(values)) elif isinstance(values, (list, tuple)): # 检查是否是 [min, max, step] 格式 if len(values) == 3 and all(isinstance(v, (int, float)) for v in values): min_val, max_val, step = values # 生成范围 if isinstance(step, int) and isinstance(min_val, int): param_values.append(list(range(int(min_val), int(max_val) + 1, int(step)))) else: # 浮点数范围 import numpy as np param_values.append(list(np.arange(min_val, max_val + step/2, step))) else: # 离散值列表 param_values.append(list(values)) else: # 单个值也转为列表 param_values.append([values]) # 生成笛卡尔积 combinations = [] for combo in itertools.product(*param_values): param_dict = dict(zip(param_names, combo)) combinations.append(param_dict) logger.info(f"参数空间大小: {len(combinations)} 组参数组合") return combinations def validate_param_space(param_space: Dict[str, Union[range, List]]) -> bool: """验证参数空间定义的合法性。 检查: - 参数空间不为空 - 所有参数至少有一个取值 - range 对象有效(start < stop) 返回: bool: True 表示合法,False 表示不合法 """ if not param_space: logger.error("参数空间为空") return False for param_name, values in param_space.items(): if isinstance(values, range): if len(values) == 0: logger.error(f"参数 {param_name} 的 range 为空: {values}") return False elif isinstance(values, (list, tuple)): if len(values) == 0: logger.error(f"参数 {param_name} 的列表为空") return False else: logger.warning(f"参数 {param_name} 的值类型未知: {type(values)},将作为单个值处理") return True def estimate_combinations_count(param_space: Dict[str, Union[range, List]]) -> int: """估算参数组合总数(不实际生成)。 用于在生成前评估计算量。 返回: int: 参数组合总数 """ if not param_space: return 0 total = 1 for values in param_space.values(): if isinstance(values, range): total *= len(values) elif isinstance(values, (list, tuple)): # 检查是否是 [min, max, step] 格式 if len(values) == 3 and all(isinstance(v, (int, float)) for v in values): min_val, max_val, step = values total *= int((max_val - min_val) / step) + 1 else: total *= len(values) else: total *= 1 return total def apply_param_constraints( combinations: List[Dict[str, Any]], constraint_func: callable ) -> List[Dict[str, Any]]: """应用参数约束,过滤无效的参数组合。 参数: combinations: 参数组合列表 constraint_func: 约束函数,输入params字典,返回True/False 返回: 过滤后的参数组合列表 示例: >>> combinations = [{"a": 1, "b": 2}, {"a": 3, "b": 2}] >>> constraint = lambda p: p["a"] < p["b"] >>> apply_param_constraints(combinations, constraint) [{"a": 1, "b": 2}] """ if constraint_func is None: return combinations original_count = len(combinations) filtered = [combo for combo in combinations if constraint_func(combo)] filtered_count = original_count - len(filtered) if filtered_count > 0: logger.info(f"约束条件过滤掉 {filtered_count} 组参数,剩余 {len(filtered)} 组") return filtered