{"record":{"id":"743f31db631ff6ca","repo":"ZhuLinsen/daily_stock_analysis","slug":"eval-window-days-must-be-positive","errorCode":null,"errorMessage":"eval_window_days must be positive","messagePattern":"eval_window_days must be positive","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/core/backtest_engine.py","lineNumber":188,"sourceCode":"\n        Notes:\n        - Daily bars cannot determine intraday ordering. If stop-loss and\n          take-profit are both touched in the same bar, we record\n          first_hit=\"ambiguous\" and assume stop-loss first for simulated exit.\n        \"\"\"\n\n        if start_price is None or start_price <= 0:\n            return {\n                \"analysis_date\": analysis_date,\n                \"operation_advice\": operation_advice,\n                \"position_recommendation\": cls.infer_position_recommendation(operation_advice),\n                \"direction_expected\": cls.infer_direction_expected(operation_advice),\n                \"eval_status\": \"error\",\n            }\n\n        eval_days = int(config.eval_window_days)\n        if eval_days <= 0:\n            raise ValueError(\"eval_window_days must be positive\")\n\n        if len(forward_bars) < eval_days:\n            return {\n                \"analysis_date\": analysis_date,\n                \"operation_advice\": operation_advice,\n                \"position_recommendation\": cls.infer_position_recommendation(operation_advice),\n                \"direction_expected\": cls.infer_direction_expected(operation_advice),\n                \"eval_status\": \"insufficient_data\",\n                \"eval_window_days\": eval_days,\n            }\n\n        window_bars = list(forward_bars[:eval_days])\n        end_close = window_bars[-1].close\n        highs = [b.high for b in window_bars if b.high is not None]\n        lows = [b.low for b in window_bars if b.low is not None]\n        max_high = max(highs) if highs else None\n        min_low = min(lows) if lows else None\n","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/core/backtest_engine.py#L170-L206","documentation":"Backtest engine guard (evaluate path 1, src/core/backtest_engine.py): eval_days = int(config.eval_window_days) must be a positive integer. Zero or negative windows are nonsensical for forward-return evaluation (you cannot measure performance over a non-positive number of bars), so the engine raises rather than producing a degenerate evaluation. Note this is reached only after start_price passed its own validity check.","triggerScenarios":"A BacktestConfig with eval_window_days = 0, a negative number, or a value that int() truncates to <= 0 (e.g. 0.5). Raised during evaluation of an operation advice with a valid positive start price.","commonSituations":"Config typo (0 instead of 30); env var EVAL_WINDOW_DAYS set to empty/0; programmatic config where a default was never applied; passing days as float < 1.","solutions":["Set eval_window_days to a positive integer (e.g. 30) in the backtest config / env.","If the value comes from user input or env, clamp/validate it (>= 1) before constructing the config.","Re-run the backtest to confirm the evaluation completes."],"exampleFix":"# before\nconfig.eval_window_days = 0\n\n# after\nconfig.eval_window_days = 30","handlingStrategy":"validation","validationCode":"eval_days = int(config.eval_window_days)\nif eval_days <= 0:\n    raise ValueError(\"eval_window_days must be a positive integer\")","typeGuard":"def is_valid_eval_window(value: object) -> bool:\n    try:\n        return int(value) > 0  # type: ignore[arg-type]\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":null,"preventionTips":["Validate eval_window_days where config is constructed, not mid-backtest","Set explicit positive defaults (e.g. 30)","Fail fast at startup on non-positive backtest config"],"tags":["backtest","config","validation"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}