CHAT_OUTPUT_BEGIN
COMMAND_ID=SIGNALBOT-260921-RECONCILIATION-FETCH-CALLSITE-READONLY-052SRC2
STATUS=OK
RC=0
HOST=pve01
MODE=read-only
COMPONENT=signalbot-reconciliation-fetch-callsite-readonly
REFERENCE_REGISTER_CHECK=OK
REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66
ERROR_REGISTER_CHECK=OK
ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0
COMMAND_SHA256=a2394e2215fc751b3d1ff578290cdb8a4ff61a0f8db3d9fe99fe41d928009871
DUPLICATE_FAILED_COMMAND_BLOCKED=false
EXECUTION_STARTED=true
CHANGE_DECLARED=false
RESULT_CONTRACT_VALID=true
RESULT_CONTRACT_STATUS=NOT_APPLICABLE
RESULT_CONTRACT_ERROR=NONE
COMMAND_RC=0
CHANGES_MADE=false
ROLLBACK_STARTED=false
ROLLBACK_RESTORED=null
MUTATION_OUTCOME=NO_MUTATION
SANITIZED=yes
SECRETS_INCLUDED=no
PRIVATE_ADDRESSES_INCLUDED=no
RAW_EVIDENCE_SHA256=bf196e2a68fb862baf3f45a031942ecc28847aa8c27da807cc17d4a3d36f7894
SANITIZED_OUTPUT_SHA256=bf196e2a68fb862baf3f45a031942ecc28847aa8c27da807cc17d4a3d36f7894
OUTPUT_BEGIN
SIGNALBOT052SRC2_RESULT={"evidence":"023ce0a041b50b866a48d9f752bd80479a34789d82f73c81d18246ed8871ef8e  /app/demonov_forward_map/bitfinex_reconciliation.py\nclass BitfinexPublicTradeHistoryClient:\n    def __init__(\n        self,\n        *,\n        fetch_json: Callable[..., object] | None = None,\n        timeout: float = 20.0,\n        max_bytes: int = 4 * 1024 * 1024,\n    ) -> None:\n        self._fetch_json = fetch_json or _stdlib_fetch_json\n        self.timeout = float(timeout)\n        self.max_bytes = int(max_bytes)\n\n    def fetch_trade_window(\n        self,\n        symbol: str,\n        *,\n        start_mts: datetime,\n        end_mts: datetime,\n    ) -> BitfinexTradeHistoryBatch:\n        if not isinstance(symbol, str) or not symbol.startswith(\"t\") or len(symbol) < 2:\n            raise DemonovError(\"Bitfinex trade history symbol invalid\")\n        start_mts = _utc(start_mts, \"start_mts\")\n        end_mts = _utc(end_mts, \"end_mts\")\n        if end_mts < start_mts:\n            raise DemonovError(\"Bitfinex trade history interval invalid\")\n        start_ms = int(start_mts.timestamp() * 1000)\n        end_ms = int(end_mts.timestamp() * 1000)\n        query = urlencode({\"start\": start_ms, \"end\": end_ms, \"sort\": 1, \"limit\": BITFINEX_PUBLIC_HISTORY_LIMIT})\n        url = f\"{BITFINEX_PUBLIC_TRADES_BASE}/{quote(symbol, safe='')}/hist?{query}\"\n        try:\n            payload = self._fetch_json(\n                url,\n                timeout=self.timeout,\n                max_bytes=self.max_bytes,\n            )\n        except DemonovError:\n            raise\n        except Exception as exc:\n            raise DemonovError(\n                \"Bitfinex public trade history request failed\"\n            ) from exc\n        if not isinstance(payload, list):\n            raise DemonovError(\"Bitfinex public trade history payload invalid\")\n\n        normalized: list[BitfinexCanonicalTradeV1] = []\n        raw_rows: list[list[object]] = []\n        seen: dict[str, str] = {}\n        order_keys: list[tuple[datetime, int]] = []\n        for raw in payload:\n            if not isinstance(raw, list):\n                raise DemonovError(\"Bitfinex REST trade row malformed\")\n            trade = normalize_bitfinex_rest_trade(raw)\n            raw_rows.append(list(raw[:4]))\n            prior = seen.get(trade.trade_identity)\n            if prior is not None:\n                if prior != trade.semantic_hash:\n                    raise DemonovError(\"Bitfinex REST trade semantic conflict\")\n                continue\n            seen[trade.trade_identity] = trade.semantic_hash\n            normalized.append(trade)\n            order_keys.append((trade.mts, trade.trade_id))\n        if order_keys != sorted(order_keys):\n            raise DemonovError(\"Bitfinex public trade history not ascending\")\n\n        evidence_sha256 = canonical_hash(\n            {\n                \"schema\": \"CR0116_BITFINEX_RECONCILIATION_REST_EVIDENCE_V1\",\n                \"symbol\": symbol,\n                \"start_mts\": start_mts,\n                \"end_mts\": end_mts,\n                \"raw_row_count\": len(payload),\n                \"rows\": raw_rows,\n            }\n        )\n        return BitfinexTradeHistoryBatch(\n            symbol=symbol,\n            start_mts=start_mts,\n            end_mts=end_mts,\n            raw_row_count=len(payload),\n            trades=tuple(normalized),\n            evidence_sha256=evidence_sha256,\n        )\n","guest_exitcode":0,"outer_rc":0,"stderr_present":false}
TASK_RESULT=PASS_SIGNALBOT_052SRC2_CALLSITE_CAPTURED
HOMELAB_RESULT_CONTRACT={"version":1,"command_id":"SIGNALBOT-260921-RECONCILIATION-FETCH-CALLSITE-READONLY-052SRC2","status":"OK","changes_made":false,"rollback_started":false,"rollback_restored":null}

OUTPUT_END
CHAT_OUTPUT_END
