From b9d9a6373b4e3eff7a08792471f11cd650ed1eea Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 09:43:46 +0000 Subject: [PATCH] Refactor: consolidate duplicated _elapsed_ms and remove one-liner wrapper _elapsed_ms() was copy-pasted identically into all six validation check modules (ruff_check, mypy_check, interface_check, lookahead_check, feature_check, resource_check). Extracted to validation/_utils.py and replaced each local definition with an import. BacktestEngine._parse_zone() was a one-line wrapper that called the module-level _parse_zone() function with self._products[0]. Inlined the call at the single use site and removed the method. https://claude.ai/code/session_01XqYtc2t1oCV8eTmXN8aaiY --- src/nexa_backtest/engines/backtest.py | 6 +----- src/nexa_backtest/validation/_utils.py | 10 ++++++++++ src/nexa_backtest/validation/feature_check.py | 5 +---- src/nexa_backtest/validation/interface_check.py | 5 +---- src/nexa_backtest/validation/lookahead_check.py | 5 +---- src/nexa_backtest/validation/mypy_check.py | 5 +---- src/nexa_backtest/validation/resource_check.py | 5 +---- src/nexa_backtest/validation/ruff_check.py | 5 +---- 8 files changed, 17 insertions(+), 29 deletions(-) create mode 100644 src/nexa_backtest/validation/_utils.py diff --git a/src/nexa_backtest/engines/backtest.py b/src/nexa_backtest/engines/backtest.py index ee1f59b..1450297 100644 --- a/src/nexa_backtest/engines/backtest.py +++ b/src/nexa_backtest/engines/backtest.py @@ -840,7 +840,7 @@ def run(self) -> BacktestResult: registry.register(provider) # Determine the zone (first product wins) - zone = self._parse_zone() + zone = _parse_zone(self._products[0]) all_fills: list[Fill] = [] equity_snapshots: list[EquitySnapshot] = [] @@ -1315,10 +1315,6 @@ def _auction_time(delivery_date: date) -> datetime: delivery_dt = datetime.combine(delivery_date, time(0, 0), tzinfo=UTC) return delivery_dt + _AUCTION_OFFSET - def _parse_zone(self) -> str: - """Extract zone from the first product spec.""" - return _parse_zone(self._products[0]) - def _mtu_to_product_id(mtu_start: datetime, zone: str) -> str: """Convert an MTU start time to a Nord Pool IDC product ID. diff --git a/src/nexa_backtest/validation/_utils.py b/src/nexa_backtest/validation/_utils.py new file mode 100644 index 0000000..544d3ca --- /dev/null +++ b/src/nexa_backtest/validation/_utils.py @@ -0,0 +1,10 @@ +"""Shared utilities for validation check modules.""" + +from __future__ import annotations + +import time + + +def _elapsed_ms(start: float) -> int: + """Return milliseconds elapsed since ``start`` (from :func:`time.monotonic`).""" + return int((time.monotonic() - start) * 1000) diff --git a/src/nexa_backtest/validation/feature_check.py b/src/nexa_backtest/validation/feature_check.py index a9cf29f..7110cbe 100644 --- a/src/nexa_backtest/validation/feature_check.py +++ b/src/nexa_backtest/validation/feature_check.py @@ -13,6 +13,7 @@ from pathlib import Path from nexa_backtest.exchanges.base import ExchangeCapabilities +from nexa_backtest.validation._utils import _elapsed_ms from nexa_backtest.validation.runner import StepResult # Map exchange name → capabilities factory. @@ -229,7 +230,3 @@ def _extract_numeric(node: ast.expr) -> float | int | None: ): return -node.operand.value return None - - -def _elapsed_ms(start: float) -> int: - return int((time.monotonic() - start) * 1000) diff --git a/src/nexa_backtest/validation/interface_check.py b/src/nexa_backtest/validation/interface_check.py index c1422e5..1f2ba23 100644 --- a/src/nexa_backtest/validation/interface_check.py +++ b/src/nexa_backtest/validation/interface_check.py @@ -11,6 +11,7 @@ import time from pathlib import Path +from nexa_backtest.validation._utils import _elapsed_ms from nexa_backtest.validation.runner import StepResult # Expected hook signatures for SimpleAlgo methods. @@ -309,7 +310,3 @@ def _check_multiple_definitions( f"{filename}: Multiple algo definitions found: {', '.join(names)}. " "Place each algo in its own file. Ambiguous file cannot be loaded." ) - - -def _elapsed_ms(start: float) -> int: - return int((time.monotonic() - start) * 1000) diff --git a/src/nexa_backtest/validation/lookahead_check.py b/src/nexa_backtest/validation/lookahead_check.py index dbf4f8c..8c00d99 100644 --- a/src/nexa_backtest/validation/lookahead_check.py +++ b/src/nexa_backtest/validation/lookahead_check.py @@ -12,6 +12,7 @@ import time from pathlib import Path +from nexa_backtest.validation._utils import _elapsed_ms from nexa_backtest.validation.runner import StepResult # Large lookback threshold for get_signal_history() in number of MTUs. @@ -222,7 +223,3 @@ def _check_sort_iloc( "rows that are chronologically in the future after sorting. " "Verify this does not introduce look-ahead bias." ) - - -def _elapsed_ms(start: float) -> int: - return int((time.monotonic() - start) * 1000) diff --git a/src/nexa_backtest/validation/mypy_check.py b/src/nexa_backtest/validation/mypy_check.py index c9867da..9ba8bac 100644 --- a/src/nexa_backtest/validation/mypy_check.py +++ b/src/nexa_backtest/validation/mypy_check.py @@ -12,6 +12,7 @@ import time from pathlib import Path +from nexa_backtest.validation._utils import _elapsed_ms from nexa_backtest.validation.runner import StepResult # mypy output line pattern: filename:line: severity: message [error-code] @@ -109,7 +110,3 @@ def run(self, algo_path: str) -> StepResult: messages=messages, duration_ms=duration, ) - - -def _elapsed_ms(start: float) -> int: - return int((time.monotonic() - start) * 1000) diff --git a/src/nexa_backtest/validation/resource_check.py b/src/nexa_backtest/validation/resource_check.py index 7de18a3..cd18154 100644 --- a/src/nexa_backtest/validation/resource_check.py +++ b/src/nexa_backtest/validation/resource_check.py @@ -10,6 +10,7 @@ import time from pathlib import Path +from nexa_backtest.validation._utils import _elapsed_ms from nexa_backtest.validation.runner import StepResult # Methods/attributes that indicate time.sleep or asyncio.sleep. @@ -338,7 +339,3 @@ def _check_mutable_globals( "({}) may leak state between backtest runs. " "Move state into the algo class or function.".format(type(val).__name__.lower()) ) - - -def _elapsed_ms(start: float) -> int: - return int((time.monotonic() - start) * 1000) diff --git a/src/nexa_backtest/validation/ruff_check.py b/src/nexa_backtest/validation/ruff_check.py index 33aaa31..4f63437 100644 --- a/src/nexa_backtest/validation/ruff_check.py +++ b/src/nexa_backtest/validation/ruff_check.py @@ -12,6 +12,7 @@ import time from pathlib import Path +from nexa_backtest.validation._utils import _elapsed_ms from nexa_backtest.validation.runner import StepResult # Ruff rule codes that indicate hard errors (not merely style issues). @@ -148,10 +149,6 @@ def run(self, algo_path: str) -> StepResult: ) -def _elapsed_ms(start: float) -> int: - return int((time.monotonic() - start) * 1000) - - def _build_config_args() -> list[str]: """Return CLI args that apply the bundled nexa ruff config.