- Drop the optimization rationale from the get_detail_data docstring; keep
only the sorted-"date" precondition as an inline comment next to the
searchsorted calls that rely on it (the O(log n)/hot-path narrative is
PR-description material, not durable docstring content).
- test_get_detail_data: build the detail frame with generate_test_data()
instead of a hand-rolled DataFrame, so the candles are roughly valid.
- Reword the test's lead comment to describe the contract under test
(detail candles in [current, current + timeframe_td), signal columns from
the main row) with the boolean mask framed as an independent oracle.
`get_detail_data` is called once per main candle that has a signal or an open
position, for every active pair, when backtesting with `--timeframe-detail`. It
located the detail candles for the current main candle by building a boolean
mask over the entire detail dataframe -> O(n_detail) per call.
The detail "date" column is sorted ascending (guaranteed by the OHLCV load
path, which cleans/resamples on "date"), so the same half-open window can be
located with two `searchsorted` lookups and an `iloc` slice -> O(log n). The
result is byte-identical to the mask.
On a 1-year single-pair BTC/USDT:USDT backtest with `--timeframe-detail 1m`,
cProfile flagged this as the largest single cumulative-time contributor in the
loop; an A/B wall-clock comparison showed ~20% faster end-to-end with identical
trade output.
Adds `test_get_detail_data` asserting equivalence to the prior mask
implementation across interior, boundary, empty, and out-of-range windows.
read_sql may return the wallet_history `bot_managed` Boolean column as an
integer dtype (e.g. MySQL/MariaDB TINYINT). results.loc[results["bot_managed"]]
then does label-based indexing instead of boolean masking, raising a KeyError
on GET /api/v1/historic_balance (HTTP 500). Coerce with .astype(bool).