{"record":{"id":"e1ac8c0e75cce6b5","repo":"microsoft/qlib","slug":"implement-reward-calculation-recipe-in-reward","errorCode":null,"errorMessage":"Implement reward calculation recipe in `reward()`.","messagePattern":"Implement reward calculation recipe in `reward\\(\\)`\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"qlib/rl/reward.py","lineNumber":31,"sourceCode":"SimulatorState = TypeVar(\"SimulatorState\")\n\n\nclass Reward(Generic[SimulatorState]):\n    \"\"\"\n    Reward calculation component that takes a single argument: state of simulator. Returns a real number: reward.\n\n    Subclass should implement ``reward(simulator_state)`` to implement their own reward calculation recipe.\n    \"\"\"\n\n    env: Optional[EnvWrapper] = None\n\n    @final\n    def __call__(self, simulator_state: SimulatorState) -> float:\n        return self.reward(simulator_state)\n\n    def reward(self, simulator_state: SimulatorState) -> float:\n        \"\"\"Implement this method for your own reward.\"\"\"\n        raise NotImplementedError(\"Implement reward calculation recipe in `reward()`.\")\n\n    def log(self, name: str, value: Any) -> None:\n        assert self.env is not None\n        self.env.logger.add_scalar(name, value)\n\n\nclass RewardCombination(Reward):\n    \"\"\"Combination of multiple reward.\"\"\"\n\n    def __init__(self, rewards: Dict[str, Tuple[Reward, float]]) -> None:\n        self.rewards = rewards\n\n    def reward(self, simulator_state: Any) -> float:\n        total_reward = 0.0\n        for name, (reward_fn, weight) in self.rewards.items():\n            rew = reward_fn(simulator_state) * weight\n            total_reward += rew\n            self.log(name, rew)","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/rl/reward.py#L13-L49","documentation":"NotImplementedError from the base `Reward` class (qlib/rl/reward.py:31). `Reward.__call__` is `@final` and delegates to `reward(simulator_state)`; the base implementation intentionally raises so that subclasses must supply their own reward recipe.","triggerScenarios":"Instantiating `Reward()` directly, or subclassing `Reward` without overriding `reward()`, and then running a trainer/vessel that invokes the reward during rollout (the env wrapper calls it every step).","commonSituations":"Copy-pasting an existing reward class and renaming it while forgetting to rename/keep the `reward` method; using a reward stub during prototyping and then running full training; method signature typo (e.g. `rewards()` or wrong arg count still counts as missing override).","solutions":["Subclass `Reward` and implement `def reward(self, simulator_state) -> float` with your calculation.","For combining existing rewards, use `RewardCombination({name: (reward, weight)})` instead of writing a new class.","Make sure the override signature matches exactly (`reward(self, simulator_state: SimulatorState) -> float`)."],"exampleFix":"// before\nclass MyReward(Reward):\n    pass  # forgot to implement -> NotImplementedError at first env step\n// after\nclass MyReward(Reward):\n    def reward(self, simulator_state: SimulatorState) -> float:\n        return float(simulator_state.position.abs().max())","handlingStrategy":"validation","validationCode":"from qlib.rl.reward import Reward\n\ndef reward_implemented(reward_cls) -> bool:\n    return reward_cls.reward is not Reward.reward","typeGuard":"def is_concrete_reward(r) -> bool:\n    from qlib.rl.reward import Reward\n    return isinstance(r, Reward) and type(r).reward is not Reward.reward","tryCatchPattern":"try:\n    value = reward_fn(simulator_state)\nexcept NotImplementedError as e:\n    raise TypeError(f\"{type(reward_fn).__name__} must implement reward()\") from e","preventionTips":["Assert `type(my_reward).reward is not Reward.reward` in vessel setup.","Prefer RewardCombination over re-implementing composite rewards.","Run a one-step rollout in CI to catch missing reward overrides immediately."],"tags":["rl","reward","not-implemented","subclassing"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}