Skip to content

Fix barssince() TypeError on scalar input (e.g. cross()) - #1422

Open
Geochelone666 wants to merge 3 commits into
kernc:masterfrom
Geochelone666:fix-1259-barssince-scalar
Open

Geochelone666 wants to merge 3 commits into
kernc:masterfrom
Geochelone666:fix-1259-barssince-scalar

Conversation

@Geochelone666

Copy link
Copy Markdown

Bug

barssince(cross(...)) raised TypeError: object of type 'numpy.bool_' has no len() because cross() returns a scalar numpy.bool_ while barssince expected a sequence (Sequence[bool]).

Fix

barssince now accepts scalar bool/np.bool_ gracefully: scalar True returns 0 ("just happened"), scalar False returns default. Docstring updated to document the semantics. Behavior for sequence inputs is unchanged.

Tests

3 new regression tests in backtesting/test/_test.py:

  • test_barssince_scalar (bool/np.bool_ True/False, default inf/-1)
  • test_barssince_cross (the exact issue scenario: barssince(cross(...)))
  • test_barssince_sequences (list/tuple/ndarray behavior unchanged)

Verified: old code raises the reported TypeError; new code handles scalars and preserves sequence behavior.

Note: patch prepared with AI assistance (Claude Codex) and reviewed by a human coordinator.

Fixes #1259.

@arhancanli

Copy link
Copy Markdown

Reproduced on the released 0.6.6 in a clean venv: barssince(cross([0,2],[1,1])) raises TypeError: object of type 'bool' has no len() (cross returns a plain bool), and the same happens for np.True_, so the isinstance check covers the two types that matter here.

Two small things I would look at:

  • barssince(1) and barssince(np.array(True)) (0-d) still raise TypeError (len() of an int / unsized object), so a truthy int from a &/and on ints slips through. If the intent is "any scalar", np.ndim(condition) == 0 with bool(condition) handles all of these in one branch; if it is only bools, the docstring sentence could say so.
  • For a scalar the result is only ever 0 or default, so barssince(cross(a, b)) < 5 is just cross(a, b) in disguise. The docstring note already hints at this; an example showing the sequence form (barssince(crossed_series)) next to it would stop people from reading the scalar answer as a history.

The new tests look right to me; test_barssince_cross covers both crossing directions and the no-cross case.

@Geochelone666

Copy link
Copy Markdown
Author

Thanks for the thorough review! Addressed both points in 8691f7e:

  1. The scalar branch is now np.ndim(condition) == 0 instead of the isinstance check, so any 0-d input is handled in one branch — barssince(1), barssince(np.array(True)), numpy scalars, etc. all return 0/default as appropriate.
  2. The docstring now states the intent explicitly ("any scalar (0-d)") and shows the sequence form next to the scalar form, noting a scalar answer is only ever 0 or default.

Extended test_barssince_scalar with int / numpy-scalar / 0-d-array cases; all 16 TestLib tests pass.

@arhancanli

Copy link
Copy Markdown

Read the new diff: np.ndim(condition) == 0 covers the cases I listed, and the extra tests (0-d arrays, numpy scalars, cross output) match what failed before. One side effect to be aware of: np.ndim(None) and np.ndim("a") are also 0, so barssince(None) now returns default silently where it used to raise. I think that is acceptable, but it is the only input class whose behaviour changes without being a bool or number. I only read the diff and checked np.ndim on those inputs; I did not run the suite.

Lock in the 0-d branch side effect noted in review: barssince(None)
returns default (falsy), barssince('a') returns 0 (truthy); both used
to raise TypeError. Extends test_barssince_scalar.
@Geochelone666

Copy link
Copy Markdown
Author

Good catch, thanks. I've added regression tests locking this in: barssince(None) returns default (falsy), barssince("a") returns 0 (truthy) — both used to raise TypeError. I agree it's acceptable: as the docstring says, a scalar answer is only ever 0 or default, so this is just the documented scalar semantics applied uniformly, no silent wrong-history risk.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

backtesting.lib combing barssince and cross?

2 participants