{"record":{"id":"e861c3928178b4b9","repo":"microsoft/qlib","slug":"trade-info-is-necessary-in-atomic-executor","errorCode":null,"errorMessage":"trade_info is necessary in atomic executor","messagePattern":"trade_info is necessary in atomic executor","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"qlib/backtest/account.py","lineNumber":381,"sourceCode":"            - else, aggregate indicators with inner indicators\n        outer_trade_decision: BaseTradeDecision\n            external trade decision\n        trade_info : List[(Order, float, float, float)], optional\n            trading information, by default None\n            - necessary if atomic is True\n            - list of tuple(order, trade_val, trade_cost, trade_price)\n        inner_order_indicators : Indicator, optional\n            indicators of inner executor, by default None\n            - necessary if atomic is False\n            - used to aggregate outer indicators\n        decision_list: List[Tuple[BaseTradeDecision, pd.Timestamp, pd.Timestamp]] = None,\n            The decision list of the inner level: List[Tuple[<decision>, <start_time>, <end_time>]]\n            The inner level\n        indicator_config : dict, optional\n            config of calculating indicators, by default {}\n        \"\"\"\n        if atomic is True and trade_info is None:\n            raise ValueError(\"trade_info is necessary in atomic executor\")\n        elif atomic is False and inner_order_indicators is None:\n            raise ValueError(\"inner_order_indicators is necessary in un-atomic executor\")\n\n        # update current position and hold bar count in each bar end\n        self.update_current_position(trade_start_time, trade_end_time, trade_exchange)\n\n        if self.is_port_metr_enabled():\n            # portfolio_metrics is portfolio related analysis\n            self.update_portfolio_metrics(trade_start_time, trade_end_time)\n            self.update_hist_positions(trade_start_time)\n\n        # update indicator in each bar end\n        self.update_indicator(\n            trade_start_time=trade_start_time,\n            trade_exchange=trade_exchange,\n            atomic=atomic,\n            outer_trade_decision=outer_trade_decision,\n            trade_info=trade_info,","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/backtest/account.py#L363-L399","documentation":"DatasetCache.update (qlib/data/cache.py:464) is the abstract method that extends dataset cache files to the latest calendar, returning 0 (updated) / 1 (no update needed) / 2 (failure). The base class has no implementation, so calling it raises NotImplementedError (the message text about 'expression cache' is a copy-paste from ExpressionCache but the intent is dataset cache refresh).","triggerScenarios":"Invoking dataset-cache refresh (directly or via scripts that call DatasetCache.update after new trading data is dumped) on the base DatasetCache or a subclass that does not override update.","commonSituations":"Daily incremental pipeline: dump new bar data, then refresh dataset caches; custom cache backend where only read paths were implemented.","solutions":["Use DiskDatasetCache.update(cache_uri, freq), which is implemented for the disk format","Override update(self, cache_uri: Union[str, Path], freq: str = 'day') -> int in your subclass","If incremental update is unsupported by your backend, delete and regenerate the dataset cache instead"],"exampleFix":"# before\nDatasetCache(provider).update(cache_uri, \"day\")  # NotImplementedError\n\n# after\nfrom qlib.data.cache import DiskDatasetCache\nDiskDatasetCache.update(cache_uri, \"day\")  # classmethod-friendly in shipped impl","handlingStrategy":"try-catch","validationCode":"from qlib.data.cache import DatasetCache\n\nassert MyDSCache.update is not DatasetCache.update, \"update() not implemented\"","typeGuard":null,"tryCatchPattern":"try:\n    status = cache.update(cache_uri, freq)\nexcept NotImplementedError:\n    shutil.rmtree(cache_dir, ignore_errors=True)\n    regenerate_dataset_cache(insts, fields, freq)","preventionTips":["Automate daily cache refresh with a fallback-to-rebuild policy","Only use cache classes whose update() is implemented (DiskDatasetCache)","Treat status code 2 (failure) the same as needing a rebuild"],"tags":["qlib","cache","custom-backend","not-implemented"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}