Skip to content

avg_dollar_volume multiplies today's price by a three-month average volume #4

Description

@Ben05-sys

In app/universe.py:

df["avg_dollar_volume"] = df["price"] * df["avg_volume_3m"]

That is today's price against a three-month average volume — two different clocks in one number — and it is then classified STATIC_SAFE in app/screen.py, meaning a screen can prefilter on it before live quotes are fetched.

The intent is defensible and documented: it is a liquidity floor, not a reading of the tape. avgdvol > 20m means "this name normally trades enough to get out of", and half a day of price drift genuinely does not change that answer. That is why it is static-safe while today's dollar_volume deliberately is not.

But the docstring does not say that the two inputs come from different periods, and a reader checking whether the field means what they think will not find out from the code. For a name whose price has halved since the quarter began, the figure overstates historical liquidity by 2x.

What to decide

Either is a good outcome — pick one and argue for it in the PR:

  • Document it. Extend the comment in derive() to state the mixed time base explicitly and why it is the right trade for a liquidity floor. Cheapest, and possibly correct.
  • Fix it. Use a longer-run price so both factors describe the same period. Needs a price series, which is the same missing piece as Indicators inside the expression engine (RSI, MACD, ATR) #3 — so it may be worth doing after that.

If you change the number

Anything that shifts what avgdvol returns changes which rows every liquidity screen matches, so:

  • Add a test with hand-computed values showing old versus new for a name whose price has moved a lot.
  • Check the dvol dropdown in FILTER_SPECS still compiles to thresholds that mean the same thing.
  • tests/test_screen.py already asserts avgdvol > 20m narrows before re-pricing. Keep that property or explain why it should change.

Small, self-contained, and it makes you read the most interesting part of the screener — the static-versus-live split.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood first issueGood for newcomers

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions