{"record":{"id":"a6cf7c400fe3c654","repo":"nautechsystems/nautilus_trader","slug":"backtestengine-requires-testclock","errorCode":null,"errorMessage":"BacktestEngine requires TestClock","messagePattern":"BacktestEngine requires TestClock","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/engine.rs","lineNumber":2065,"sourceCode":"        }\n    }\n\n    fn init_command_senders() {\n        replace_data_cmd_sender(Arc::new(SyncDataCommandSender));\n        replace_exec_cmd_sender(Arc::new(SyncTradingCommandSender));\n    }\n\n    fn advance_clock_on_accumulator(\n        accumulator: &mut TimeEventAccumulator,\n        clock: &Rc<RefCell<dyn Clock>>,\n        to_time_ns: UnixNanos,\n        set_time: bool,\n    ) {\n        let mut clock_ref = clock.borrow_mut();\n        let test_clock = clock_ref\n            .as_any_mut()\n            .downcast_mut::<TestClock>()\n            .expect(\"BacktestEngine requires TestClock\");\n        accumulator.advance_clock(test_clock, to_time_ns, set_time);\n    }\n\n    fn set_all_clocks_time(clocks: &[Rc<RefCell<dyn Clock>>], ts: UnixNanos) {\n        for clock in clocks {\n            let mut clock_ref = clock.borrow_mut();\n            let test_clock = clock_ref\n                .as_any_mut()\n                .downcast_mut::<TestClock>()\n                .expect(\"BacktestEngine requires TestClock\");\n            test_clock.set_time(ts);\n        }\n    }\n\n    #[rustfmt::skip]\n    fn log_pre_run(&self) {\n        log_info!(\"=================================================================\", color = LogColor::Cyan);\n        log_info!(\" BACKTEST PRE-RUN\", color = LogColor::Cyan);","sourceCodeStart":2047,"sourceCodeEnd":2083,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/engine.rs#L2047-L2083","documentation":"BacktestEngine's time-advancement path downcasts the provided dyn Clock to a concrete TestClock because the engine's event scheduling is driven by the mutable time-setting API of TestClock. If the borrowed clock is not actually a TestClock (e.g. a LiveClock or custom Clock), the downcast returns None and this panic fires.","triggerScenarios":"Calling advance_time (or the internal advance-clock helper) with a clock Rc<RefCell<dyn Clock>> that is not a TestClock — e.g. wiring a LiveClock, a mock Clock other than TestClock, or a wrapped clock into the backtest engine and then advancing time.","commonSituations":"Mixing live-trading clock setup code with the backtest engine; constructing the engine kernel with a custom Clock implementation; copy-pasted kernel builders shared between live and backtest code paths.","solutions":["Use TestClock::new() as the engine's clock (default when using BacktestEngine::new / the standard kernel builder)","Ensure any custom clock wrapper used in backtests dereferences to a real TestClock or use TestClock directly","Only call advance_time on engines built with the backtest (TestClock) kernel"],"exampleFix":"// before\nlet clock: Rc<RefCell<dyn Clock>> = Rc::new(RefCell::new(LiveClock::new()));\nengine.add_client(...); // engine built with non-TestClock\nengine.advance_time(ts, true); // panics\n// after\nlet clock: Rc<RefCell<dyn Clock>> = Rc::new(RefCell::new(TestClock::new()));\n// build engine with the TestClock, then advance_time works","handlingStrategy":"type-guard","validationCode":"use std::any::Any;\nfn is_test_clock(clock: &Rc<RefCell<dyn Clock>>) -> bool {\n    clock.borrow().as_any().downcast_ref::<TestClock>().is_some()\n}","typeGuard":"fn as_test_clock(clock: &mut Rc<RefCell<dyn Clock>>) -> Option<std::cell::RefMut<TestClock>> {\n    RefMut::filter_map(clock.borrow_mut(), |c| c.as_any_mut().downcast_mut::<TestClock>()).ok()\n}","tryCatchPattern":"// Downcast failure panics; pre-check instead:\nif as_test_clock(&clock).is_none() {\n    panic!(\"backtest engine must be built with TestClock\");\n}\nengine.advance_time(ts, true);","preventionTips":["Always build backtest engines with TestClock::new()","Never register LiveClock or custom Clocks with the backtest kernel","Keep backtest and live kernel builders separate","Downcast-check clocks before calling advance_time"],"tags":["rust","panic","backtest","clock","downcast"],"backgroundTag":"incompatible-source-type","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}