Files
mt_mt5_backertest/scripts/optimizer/parameter_constraint.py
T
2026-07-06 03:32:12 +08:00

87 lines
3.1 KiB
Python

import re
from typing import List, Dict, Any, Callable
class ParameterConstraintEngine:
def __init__(self):
self.constraints = []
self.condition_functions = []
def add_constraint(self, name: str, condition: str, error_message: str = ''):
self.constraints.append({
'name': name,
'condition': condition,
'error_message': error_message or 'Constraint violated: ' + name
})
def add_condition_from_config(self, param_name: str, condition_str: str):
if not condition_str:
return
condition_str = condition_str.strip()
self.constraints.append({
'name': param_name,
'condition': condition_str,
'error_message': 'Parameter constraint violated: ' + param_name
})
def _parse_condition(self, condition: str) -> Callable[[Dict], bool]:
condition = condition.strip()
patterns = [
(r'^(\w+)\s*>\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) > p.get(m.group(2), 0)),
(r'^(\w+)\s*<\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) < p.get(m.group(2), 0)),
(r'^(\w+)\s*>=\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) >= p.get(m.group(2), 0)),
(r'^(\w+)\s*<=\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) <= p.get(m.group(2), 0)),
(r'^(\w+)\s*==\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) == p.get(m.group(2), 0)),
(r'^(\w+)\s*!=\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) != p.get(m.group(2), 0)),
(r'^(\w+)\s*>\s*(\d+)$', lambda m, p: p.get(m.group(1), 0) > int(m.group(2))),
(r'^(\w+)\s*<\s*(\d+)$', lambda m, p: p.get(m.group(1), 0) < int(m.group(2))),
]
for pattern, func in patterns:
match = re.match(pattern, condition)
if match:
return lambda params, m=match, f=func: f(m, params)
return lambda params: True
def _is_valid_combination(self, params: Dict) -> bool:
for constraint in self.constraints:
condition = constraint['condition']
if not condition:
continue
parse_func = self._parse_condition(condition)
if not parse_func(params):
return False
return True
def filter_valid_combinations(self, combinations: List[Dict]) -> List[Dict]:
valid = []
invalid_count = 0
for combo in combinations:
if self._is_valid_combination(combo):
valid.append(combo)
else:
invalid_count += 1
if invalid_count > 0:
print('Filtered ' + str(invalid_count) + ' invalid combinations')
return valid
def get_constraint_count(self) -> int:
return len(self.constraints)
def create_constraint_engine(ea_config) -> ParameterConstraintEngine:
engine = ParameterConstraintEngine()
for param_name, param in ea_config.parameters.items():
if param.condition:
engine.add_condition_from_config(param_name, param.condition)
return engine