8from typing
import AbstractSet, Mapping, NoReturn, Sequence
124 bindings: Sequence[SymbolBinding],
126 search_space: Mapping[str, ParameterSpec],
127 referenced_dataset_ids: Sequence[str],
128 datasets: Sequence[DatasetDeclaration],
129) -> tuple[ResolvedSymbolBinding, ...]:
130 """Check a complete effective domain without loading data or sampling candidates.
132 The caller supplies the effective search space after its ordinary input validation.
133 This component neither admits new StudySpec fields nor bypasses manifest validation.
134 Additional dataset declarations are checked but need not be binding targets.
137 declarations: dict[str, DatasetDeclaration] = {}
138 for declaration
in datasets:
139 normalized = _dataset(declaration)
140 if normalized.id
in declarations:
142 "input_symbol", f
"datasets: duplicate dataset id {normalized.id!r}"
144 declarations[normalized.id] = normalized
146 references: set[str] = set()
147 for dataset_id
in referenced_dataset_ids:
148 _text(dataset_id,
"strategy.datasets",
"input_symbol")
149 if dataset_id
in references:
151 "input_symbol", f
"strategy.datasets: duplicates include {dataset_id!r}"
153 if dataset_id
not in declarations:
155 "input_symbol", f
"strategy.datasets: unknown dataset {dataset_id!r}"
157 references.add(dataset_id)
159 titles: set[str] = set()
160 resolved: list[ResolvedSymbolBinding] = []
161 for binding
in bindings:
162 where = f
"symbol_bindings.{binding.input_title}"
163 _text(binding.input_title, where,
"input_symbol")
164 if binding.input_title
in titles:
165 _invalid(
"input_symbol", f
"{where}: duplicate binding input")
166 titles.add(binding.input_title)
167 if binding.role
not in (
"chart",
"request"):
168 _invalid(
"input_symbol", f
"{where}.role: must be chart or request")
169 parameter = search_space.get(binding.input_title)
170 if parameter
is None or parameter.kind !=
"categorical":
173 f
"{where}: requires an effective categorical search input",
175 if not parameter.choices:
176 _invalid(
"input_symbol", f
"{where}.choices: domain must not be empty")
177 choices: list[str] = []
178 seen: set[str] = set()
179 for choice
in parameter.choices:
180 if not isinstance(choice, str):
183 f
"{where}.choices: symbol choice {choice!r} is not a string",
185 _text(choice, f
"{where}.choices",
"input_symbol")
188 "input_symbol", f
"{where}.choices: duplicate choice {choice!r}"
191 choices.append(choice)
193 mapping: dict[str, str] = {}
194 targets: set[str] = set()
195 for choice, dataset_id
in binding.datasets:
196 _text(choice, f
"{where}.datasets choice",
"input_symbol")
197 _text(dataset_id, f
"{where}.datasets[{choice!r}]",
"input_symbol")
198 if choice
in mapping:
199 _invalid(
"input_symbol", f
"{where}.datasets: duplicate key {choice!r}")
200 mapping[choice] = dataset_id
201 if set(mapping) != seen:
204 f
"{where}.datasets: keys must equal choices exactly; "
205 f
"missing={sorted(seen - set(mapping))!r}, extra={sorted(set(mapping) - seen)!r}",
207 selected: list[DatasetDeclaration] = []
208 for choice
in choices:
209 dataset_id = mapping[choice]
210 if dataset_id
in targets:
212 "input_symbol", f
"{where}.datasets: duplicate target {dataset_id!r}"
214 targets.add(dataset_id)
215 if dataset_id
not in declarations:
218 f
"{where}.datasets[{choice!r}]: unknown dataset {dataset_id!r}",
220 if dataset_id
not in references:
223 f
"{where}.datasets[{choice!r}]: unreferenced dataset {dataset_id!r}",
225 declaration = declarations[dataset_id]
226 if declaration.symbol != choice:
229 f
"{where}.datasets[{choice!r}]: dataset {dataset_id!r} declares "
230 f
"symbol {declaration.symbol!r}",
232 selected.append(declaration)
235 binding.input_title, binding.role, tuple(choices), tuple(selected)
238 if bindings
and sum(binding.role ==
"chart" for binding
in bindings) != 1:
239 _invalid(
"input_symbol",
"symbol_bindings: exactly one chart role is required")
240 return tuple(resolved)
244 bindings: Sequence[ResolvedSymbolBinding],
248 approved_alignment_modes: AbstractSet[str],
250 """Refuse undecided policy combinations; callers must supply all approved modes."""
256 "pruner", f
"execution.pruner {pruner!r}: undecided policy for bound data"
258 currencies: dict[str, str] = {}
259 for binding
in bindings:
260 for declaration
in binding.datasets:
261 currencies[declaration.id] = declaration.syminfo.currency
262 if len(set(currencies.values())) > 1:
265 f
"objective: undecided quote currency ranking policy for datasets {currencies!r}",
267 if alignment_mode
not in approved_alignment_modes:
270 f
"data_comparison.alignment {alignment_mode!r}: missing approval in approved modes",
tuple[ResolvedSymbolBinding,...] validate_symbol_bindings(Sequence[SymbolBinding] bindings, *Mapping[str, ParameterSpec] search_space, Sequence[str] referenced_dataset_ids, Sequence[DatasetDeclaration] datasets)