{"record":{"id":"0609d95900d330ca","repo":"HKUDS/Vibe-Trading","slug":"kind-self-kind-r-must-have-a-direction-amount","errorCode":null,"errorMessage":"kind={self.kind!r} must have a {direction} amount under the holder-perspective sign convention, got {amount!r}. Flip the sign, or use a distinct kind if this flow is genuinely two-directional (e.g. 'recallable_distribution').","messagePattern":"kind=(.+?) must have a (.+?) amount under the holder-perspective sign convention, got (.+?)\\. Flip the sign, or use a distinct kind if this flow is genuinely two-directional \\(e\\.g\\. 'recallable_distribution'\\)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/entities/cashflow.py","lineNumber":173,"sourceCode":"\n        try:\n            amount = float(self.amount)\n        except (TypeError, ValueError) as exc:\n            raise ValueError(\n                f\"amount must be numeric, got {self.amount!r}\"\n            ) from exc\n        if not math.isfinite(amount):\n            raise ValueError(\n                f\"amount must be a finite number, got {self.amount!r}; a missing \"\n                \"value must be fixed at the source, not carried as NaN\"\n            )\n        object.__setattr__(self, \"amount\", amount)\n\n        required_sign = KIND_DIRECTION.get(self.kind)\n        if required_sign is not None and amount != 0.0:\n            if (amount > 0) != (required_sign > 0):\n                direction = \"positive (cash in)\" if required_sign > 0 else \"negative (cash out)\"\n                raise ValueError(\n                    f\"kind={self.kind!r} must have a {direction} amount under the \"\n                    f\"holder-perspective sign convention, got {amount!r}. Flip the \"\n                    \"sign, or use a distinct kind if this flow is genuinely \"\n                    \"two-directional (e.g. 'recallable_distribution').\"\n                )\n\n        if not isinstance(self.metadata, Mapping):\n            raise ValueError(\n                f\"metadata must be a mapping, got {type(self.metadata).__name__}\"\n            )\n        object.__setattr__(self, \"metadata\", MappingProxyType(dict(self.metadata)))\n\n    @property\n    def is_valuation(self) -> bool:\n        \"\"\"Whether this record is a mark rather than a settled cash movement.\n\n        Returns:\n            True when ``kind`` is one of ``VALUATION_KINDS``.","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/entities/cashflow.py#L155-L191","documentation":"Each CashFlow kind has a fixed sign direction (holder perspective), e.g. dividends are cash in (positive) and purchases are cash out (negative). If the amount's sign contradicts KIND_DIRECTION for that kind, the entity refuses construction rather than silently misreporting direction.","triggerScenarios":"CashFlow(kind='purchase', amount=1000.0) when purchase requires a negative amount, or CashFlow(kind='dividend', amount=-50.0) when dividend must be positive; amount 0.0 is always allowed.","commonSituations":"Feeding raw unsigned magnitudes from a brokerage CSV where direction lives in a separate column; mixing conventions (inflow-positive vs outflow-positive) between data sources; genuinely two-directional flows modeled with the wrong kind.","solutions":["Flip the amount's sign to match the kind's required direction (see the message text for which direction is expected)","If the flow is genuinely two-directional (e.g. recallable distributions that can be clawed back), use a kind without a fixed direction such as 'recallable_distribution'","Apply the sign at ingest: amount = abs(amount) * expected_sign based on the kind mapping"],"exampleFix":"# before\nCashFlow(date=d, kind='purchase', amount=1000.0, currency='USD')\n# after\nCashFlow(date=d, kind='purchase', amount=-1000.0, currency='USD')","handlingStrategy":"validation","validationCode":"REQUIRED_SIGN = {'purchase': -1, 'dividend': 1}  # mirror KIND_DIRECTION\nrow['amount'] = abs(float(row['amount'])) * REQUIRED_SIGN.get(row['kind'], 1)","typeGuard":null,"tryCatchPattern":"try:\n    CashFlow(...)\nexcept ValueError as e:\n    if 'sign convention' in str(e):\n        row['amount'] = -row['amount']  # only if direction metadata is trusted","preventionTips":["Apply kind-based sign at ingest rather than trusting raw source signs","Document the holder perspective in your ETL docs","Add fixtures covering each kind's sign"],"tags":["python","cashflow","sign-convention","validation"],"backgroundTag":"sign-convention-violation","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}