microsoft/qlib · error · NotImplementedError

Implement reward calculation recipe in `reward()`.

Error message

Implement reward calculation recipe in `reward()`.

What it means

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.

Source

Thrown at qlib/rl/reward.py:31

SimulatorState = TypeVar("SimulatorState")


class Reward(Generic[SimulatorState]):
    """
    Reward calculation component that takes a single argument: state of simulator. Returns a real number: reward.

    Subclass should implement ``reward(simulator_state)`` to implement their own reward calculation recipe.
    """

    env: Optional[EnvWrapper] = None

    @final
    def __call__(self, simulator_state: SimulatorState) -> float:
        return self.reward(simulator_state)

    def reward(self, simulator_state: SimulatorState) -> float:
        """Implement this method for your own reward."""
        raise NotImplementedError("Implement reward calculation recipe in `reward()`.")

    def log(self, name: str, value: Any) -> None:
        assert self.env is not None
        self.env.logger.add_scalar(name, value)


class RewardCombination(Reward):
    """Combination of multiple reward."""

    def __init__(self, rewards: Dict[str, Tuple[Reward, float]]) -> None:
        self.rewards = rewards

    def reward(self, simulator_state: Any) -> float:
        total_reward = 0.0
        for name, (reward_fn, weight) in self.rewards.items():
            rew = reward_fn(simulator_state) * weight
            total_reward += rew
            self.log(name, rew)

View on GitHub (pinned to 79633dd950)

Solutions

  1. Subclass `Reward` and implement `def reward(self, simulator_state) -> float` with your calculation.
  2. For combining existing rewards, use `RewardCombination({name: (reward, weight)})` instead of writing a new class.
  3. Make sure the override signature matches exactly (`reward(self, simulator_state: SimulatorState) -> float`).

Example fix

// before
class MyReward(Reward):
    pass  # forgot to implement -> NotImplementedError at first env step
// after
class MyReward(Reward):
    def reward(self, simulator_state: SimulatorState) -> float:
        return float(simulator_state.position.abs().max())
Defensive patterns

Strategy: validation

Validate before calling

from qlib.rl.reward import Reward

def reward_implemented(reward_cls) -> bool:
    return reward_cls.reward is not Reward.reward

Type guard

def is_concrete_reward(r) -> bool:
    from qlib.rl.reward import Reward
    return isinstance(r, Reward) and type(r).reward is not Reward.reward

Try / catch

try:
    value = reward_fn(simulator_state)
except NotImplementedError as e:
    raise TypeError(f"{type(reward_fn).__name__} must implement reward()") from e

Prevention

When it happens

Trigger: 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).

Common situations: 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).

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/e1ac8c0e75cce6b5. Report an issue: GitHub.