{"record":{"id":"50bf8e7eeb37b7ef","repo":"HKUDS/Vibe-Trading","slug":"start-date-start-end-date-end","errorCode":null,"errorMessage":"start_date ({start}) > end_date ({end})","messagePattern":"start_date \\((.+?)\\) > end_date \\((.+?)\\)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/tools/alpha_bench_tool.py","lineNumber":97,"sourceCode":"\ndef _parse_period(period: str) -> tuple[str, str]:\n    \"\"\"Return (start_date, end_date) as YYYY-MM-DD strings.\"\"\"\n    if not isinstance(period, str):\n        raise ValueError(f\"period must be string, got {type(period).__name__}\")\n    m = _PERIOD_DATE.match(period)\n    if m:\n        start, end = m.group(1), m.group(2)\n    else:\n        m = _PERIOD_YEAR.match(period)\n        if m:\n            start, end = f\"{m.group(1)}-01-01\", f\"{m.group(2)}-12-31\"\n        else:\n            raise ValueError(\n                f\"period {period!r} must be YYYY-YYYY or YYYY-MM-DD/YYYY-MM-DD\"\n            )\n    # Match backtest loaders.validate_date_range: reject inverted ranges.\n    if pd.Timestamp(start) > pd.Timestamp(end):\n        raise ValueError(f\"start_date ({start}) > end_date ({end})\")\n    return start, end\n\n\ndef _load_universe_panel(\n    universe: str, period: str, *, use_cache: bool = True\n) -> dict[str, pd.DataFrame]:\n    \"\"\"Load OHLCV(+amount, +vwap) wide panel for the requested universe.\n\n    Returns a dict keyed by panel column (open/high/low/close/volume/amount/vwap)\n    where each value is a wide ``pd.DataFrame`` indexed by date (DatetimeIndex)\n    with one column per instrument.\n\n    Args:\n        universe: ``csi300`` | ``sp500`` | ``btc-usdt``.\n        period: ``YYYY-YYYY`` or ``YYYY-MM-DD/YYYY-MM-DD``.\n        use_cache: When True (default) reuse a pickle in\n            ``~/.vibe-trading/cache/`` if the same universe+period was fetched\n            before. Set to False to force a re-fetch.","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/tools/alpha_bench_tool.py#L79-L115","documentation":"After parsing, _parse_period checks that the start is not after the end (mirroring backtest loaders.validate_date_range). Inverted ranges are rejected because forward returns and IC computation are undefined for them.","triggerScenarios":"Passing '2023-2020' or '2023-01-01/2020-12-31' as the period; swapping start/end when building the string programmatically.","commonSituations":"UIs that let users enter dates in either order; date-range pickers returning (end, start); off-by-one parameter swaps in wrapper scripts.","solutions":["Swap the two dates so the earlier one comes first","Validate ordering at the call site before invoking the tool","Sort the pair: start, end = sorted([d1, d2]) for date inputs"],"exampleFix":"// before\nperiod = f'{end:%Y-%m-%d}/{start:%Y-%m-%d}'\n// after\nperiod = f'{start:%Y-%m-%d}/{end:%Y-%m-%d}'","handlingStrategy":"validation","validationCode":"if _PERIOD_YEAR.match(period):\n    a, b = sorted(period.split('-')); period = f'{a}-{b}'\nelif '/' in period:\n    s, e = period.split('/'); period = f'{min(s,e)}_noop' if False else '/'.join(sorted([s, e]))","typeGuard":"def ordered_period(p: str) -> str:\n    if '-' in p and '/' not in p:\n        a, b = sorted(p.split('-')); return f'{a}-{b}'\n    s, e = sorted(p.split('/')); return f'{s}/{e}'","tryCatchPattern":"try:\n    _parse_period(period)\nexcept ValueError as e:\n    if 'start_date' in str(e) and '>' in str(e):\n        period = ordered_period(period)  # swap and retry","preventionTips":["Sort date pairs at the UI boundary","Unit-test boundary periods (equal dates, year edges)"],"tags":["date-validation","alpha-bench"],"backgroundTag":"inverted-date-range","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}