| import re |
| from typing import Dict, Any |
| from src.data.manifest import ExperimentManifest, create_manifest |
|
|
|
|
| class LLMHypothesisParser: |
| """ |
| Typed LLM Guardrail Parser: |
| Converts natural language user research prompts (e.g. "Test whether 20-day momentum predicts 5-day returns among sector ETFs at 5 bps cost") |
| into validated, deterministic Pydantic ExperimentManifest configurations. |
| """ |
|
|
| @staticmethod |
| def parse_hypothesis(prompt: str) -> ExperimentManifest: |
| prompt_lower = prompt.lower() |
|
|
| |
| lookback_match = re.search(r"(\d+)[ -]?day momentum", prompt_lower) |
| lookback_days = int(lookback_match.group(1)) if lookback_match else 20 |
|
|
| |
| holding_match = re.search(r"(\d+)[ -]?day return", prompt_lower) |
| holding_days = int(holding_match.group(1)) if holding_match else 5 |
|
|
| |
| cost_match = re.search(r"(\d+)[ -]?bps", prompt_lower) |
| cost_bps = float(cost_match.group(1)) if cost_match else 5.0 |
|
|
| |
| universe = [ |
| "SPY", "QQQ", "IWM", "MDY", |
| "XLK", "XLF", "XLE", "XLV", "XLY", "XLP", "XLI", "XLB", "XLU", "XLC", "XLRE", |
| "SMH", "XBI", "KRE", "ITB", |
| "TLT", "IEF", "SHY", "LQD", "HYG", "TIP", |
| "GLD", "SLV", "USO", "DBA", |
| "EEM", "EFA", "FXI", "EWJ", |
| "MTUM", "USMV", "QUAL", "IWD", "IWF" |
| ] |
|
|
| manifest = create_manifest( |
| asset_universe=universe, |
| lookback_days=lookback_days, |
| holding_days=holding_days, |
| transaction_cost_bps=cost_bps, |
| feature_config={ |
| "raw_mom": True, |
| "sortino_mom": "sortino" in prompt_lower or "volatility" in prompt_lower, |
| "residual_mom": "residual" in prompt_lower, |
| "volume_z": "volume" in prompt_lower, |
| }, |
| model_config={ |
| "type": "lightgbm" if "tree" in prompt_lower or "lightgbm" in prompt_lower else "ridge", |
| "max_depth": 3, |
| "learning_rate": 0.01 |
| } |
| ) |
|
|
| return manifest |
|
|