136 lines
5.1 KiB
Python
136 lines
5.1 KiB
Python
import os
|
|
import json
|
|
from dataclasses import dataclass, field
|
|
from typing import Dict, List, Any
|
|
|
|
@dataclass
|
|
class ParameterDef:
|
|
name: str
|
|
param_type: str
|
|
default: Any = None
|
|
min_value: Any = None
|
|
max_value: Any = None
|
|
step: Any = None
|
|
precision: int = 0
|
|
optimize: bool = True
|
|
enum_options: List[Any] = field(default_factory=list)
|
|
description: str = ''
|
|
condition: str = ''
|
|
|
|
def expand_values(self) -> List[Any]:
|
|
if self.param_type == 'bool':
|
|
return [True, False]
|
|
elif self.param_type == 'enum':
|
|
return self.enum_options
|
|
elif self.param_type in ('int', 'double'):
|
|
if self.min_value is None or self.max_value is None:
|
|
return [self.default] if self.default is not None else []
|
|
values = []
|
|
current = self.min_value
|
|
while current <= self.max_value:
|
|
values.append(round(current, self.precision) if self.precision > 0 else int(current))
|
|
current += self.step
|
|
return values
|
|
return [self.default]
|
|
|
|
def is_valid_value(self, value: Any) -> bool:
|
|
if self.param_type == 'bool':
|
|
return isinstance(value, bool)
|
|
elif self.param_type == 'enum':
|
|
return value in self.enum_options
|
|
elif self.param_type in ('int', 'double'):
|
|
if self.min_value is not None and value < self.min_value:
|
|
return False
|
|
if self.max_value is not None and value > self.max_value:
|
|
return False
|
|
return True
|
|
return True
|
|
|
|
@dataclass
|
|
class EAConfig:
|
|
ea_name: str
|
|
ea_path: str
|
|
parameters: Dict[str, ParameterDef] = field(default_factory=dict)
|
|
search_strategy: str = 'auto'
|
|
optimization_criterion: str = 'profit_factor'
|
|
test_config: Dict[str, Any] = field(default_factory=dict)
|
|
walk_forward: Dict[str, Any] = field(default_factory=dict)
|
|
description: str = ''
|
|
|
|
def get_total_combinations(self) -> int:
|
|
total = 1
|
|
for param in self.parameters.values():
|
|
values = param.expand_values()
|
|
total *= len(values) if values else 1
|
|
return total
|
|
|
|
def estimate_search_time(self, tests_per_minute: float = 10) -> str:
|
|
total = self.get_total_combinations()
|
|
minutes = total / tests_per_minute
|
|
if minutes < 60:
|
|
return str(round(minutes, 1)) + ' minutes'
|
|
elif minutes < 1440:
|
|
return str(round(minutes/60, 1)) + ' hours'
|
|
else:
|
|
return str(round(minutes/1440, 1)) + ' days'
|
|
|
|
def is_complex(self) -> bool:
|
|
return len(self.parameters) > 6 or self.get_total_combinations() > 1_000_000
|
|
|
|
def load_ea_config(config_path: str) -> EAConfig:
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
config_data = json.load(f)
|
|
ea_name = config_data.get('ea_name', 'Unknown')
|
|
ea_path = config_data.get('ea_path', '')
|
|
description = config_data.get('description', '')
|
|
search_strategy = config_data.get('search_strategy', 'auto')
|
|
criterion = config_data.get('optimization_criterion', 'profit_factor')
|
|
test_config = config_data.get('test_config', {})
|
|
walk_forward = config_data.get('walk_forward', {})
|
|
parameters = {}
|
|
for param_name, param_data in config_data.get('parameters', {}).items():
|
|
param_type = param_data.get('type', 'int')
|
|
if param_type == 'enum':
|
|
param = ParameterDef(
|
|
name=param_name,
|
|
param_type='enum',
|
|
default=param_data.get('default'),
|
|
enum_options=param_data.get('options', []),
|
|
description=param_data.get('description', ''),
|
|
condition=param_data.get('condition', '')
|
|
)
|
|
else:
|
|
param = ParameterDef(
|
|
name=param_name,
|
|
param_type=param_type,
|
|
default=param_data.get('default'),
|
|
min_value=param_data.get('min'),
|
|
max_value=param_data.get('max'),
|
|
step=param_data.get('step', 1),
|
|
precision=param_data.get('precision', 0),
|
|
optimize=param_data.get('optimize', True),
|
|
description=param_data.get('description', ''),
|
|
condition=param_data.get('condition', '')
|
|
)
|
|
parameters[param_name] = param
|
|
return EAConfig(
|
|
ea_name=ea_name, ea_path=ea_path, parameters=parameters,
|
|
search_strategy=search_strategy, optimization_criterion=criterion,
|
|
test_config=test_config, walk_forward=walk_forward, description=description
|
|
)
|
|
|
|
def load_all_ea_configs(configs_dir: str) -> Dict[str, EAConfig]:
|
|
configs = {}
|
|
if not os.path.exists(configs_dir):
|
|
return configs
|
|
for filename in os.listdir(configs_dir):
|
|
if filename.endswith('.json'):
|
|
config_path = os.path.join(configs_dir, filename)
|
|
try:
|
|
config = load_ea_config(config_path)
|
|
configs[config.ea_name] = config
|
|
except Exception as e:
|
|
print('Error loading ' + config_path + ': ' + str(e))
|
|
return configs
|
|
|