This document describes the WSB (WallStreetBets) signal algorithm.
It focuses on short-term attention and sentiment dynamics and is optimized for intraweek signals. It intentionally ignores fundamentals, price action, and longer-term positioning.
v0 worked well, but it could feel "jumpy" when checking the endpoint throughout the day. Two small changes make the results noticeably more stable without changing the core logic:
-
Mixed recency decay: instead of a single half-life, we mix a fast and a slow exponential decay. The fast component keeps the list reactive to fresh chatter, while the slow component adds inertia so weekly leaders do not get replaced by short bursts.
-
Deterministic confidence: the public-facing confidence value is now a deterministic normalization of the score. In v0, it included a small time-based jitter, which made results appear to change every few minutes even when the underlying ranking did not.
Everything else (weekly structure, imbalance, direction separation, volume hygiene) stays the same.
All signals come from r/wallstreetbets events that we ingest at creation time. Each event represents either:
- a Reddit submission, or
- a Reddit comment
Upstream, we extract tickers and classify sentiment (buy, sell, neutral) per event. By the time data reaches this pipe, we deliberately do not care about:
- Reddit upvotes
- edits
- author metadata
- LLM confidence scores
Those signals are either incomplete, gameable, or misleading at ingestion time. What we keep is simple:
- timestamp
- event type (submission or comment)
- ticker
- sentiment
This simplicity is a feature, not a limitation.
All scoring is done inside ISO weeks. A week starts on Monday and ends on Sunday, ISO-style. Each event is bucketed into the week it was created in.
This gives us:
- clean weekly snapshots
- predictable resets
- the ability to talk about “this week’s picks” without hand-waving
Scores are recomputed continuously as the week progresses. Early-week signals fade naturally as new data comes in.
Not all mentions are equal, and not all mentions should age the same way.
The most important assumption in the system is that what people said in the last few hours matters more than what they said two days ago.
In v1 we still use exponential decay, but we mix two time-scales:
- a fast half-life (default: 12h) to stay reactive
- a slow half-life (default: 72h) to add inertia during the week
- a mixing factor (default: 0.75) that controls how much weight goes to the fast component
Concretely, each event gets:
recency_w = fast_weight * exp(-ln(2) * age_hours / fast_half_life_hours) + (1 - fast_weight) * exp(-ln(2) * age_hours / slow_half_life_hours)
This keeps the feed responsive to real shifts on WSB, but reduces the chance that a short burst of fresh comments completely rewrites the top 5 every hour. It also makes the Sunday snapshot read more like "how the week went" instead of "what happened in the last few minutes".
Submissions get a small boost over comments, not because they are “better”, but because:
- they require more intent and effort
- they usually anchor discussion rather than react to it
The multiplier is intentionally small. Comments still matter a lot.
At the end of the day, each (event, ticker) pair contributes a single number:
event_weight = recency_weight × event_type_multiplier
No hidden adjustments. No magic.
Once all events are weighted, we aggregate per ticker inside the week. At this stage we compute:
- total weighted volume
- number of distinct events
- weighted volume split by sentiment (
buy,sell,neutral)
Neutral mentions count toward attention, but not toward direction.
This distinction matters later.
Absolute counts alone are misleading. A ticker with 50 mentions might be dominant in a quiet week, or irrelevant in a noisy one. For that reason, we explicitly compute relative attention share:
“Out of all WSB attention this week, what fraction belongs to this ticker?”
This allows us to compare signals within the same week, which is all we actually care about. Cross-week comparisons are intentionally not the goal of this algorithm.
This is one of the most important parts of the model. We do not reward raw bullishness or bearishness, we reward imbalance.
If a ticker is:
- 50% buy / 50% sell → weak signal
- 90% buy / 10% sell → strong signal
- 95% sell with real volume → very strong (negative) signal
Mathematically, we compute an imbalance ratio that goes from 0 to 1. A small floor is applied so that ties do not completely zero out the score, but they are heavily penalized. This avoids:
- overreacting to polarized discussions
- treating divided sentiment as conviction
Direction is simple:
- buy volume > sell volume → buy
- sell volume > buy volume → sell
- tie → no signal
Strength is everything else, this separation is intentional: it allows us to talk about “strong sell signals” and “weak buy signals” without confusion.
The final score is built from three components:
- Relative attention share
- Absolute activity (log-scaled to avoid domination by spammy volume)
- Recency-weighted volume
These are combined into a base magnitude, then:
- scaled by the buy/sell imbalance
- signed by direction
The result is a single signed number:
- positive = buy
- negative = sell
- magnitude = conviction
Internally, this is what we rank on.
We do not expose the raw score directly. Instead, the final node exposes:
sentiment: buy or sell (derived from the buy vs sell imbalance)confidence: a 0..1-ish number that is simply the score normalized within the current week
Normalization is done relative to the strongest absolute score in the result set for that week, with a conservative cap (so the UI does not show false precision).
In v1, confidence is fully deterministic. v0 added a tiny time-based jitter to reduce "tie-looking" outputs, but in practice it made the endpoint feel unstable when checking it repeatedly. Removing the jitter makes the list move only when the underlying data and weighted totals actually move.
It is not:
- a price model
- a trading strategy
- an optimized backtest engine
- a claim of alpha
It is a structured way to answer:
“What is dominating attention and intent right now?”
Nothing more.
Some things are missing on purpose:
-
No Reddit upvotes
(they are incomplete and laggy at ingestion time) -
No LLM confidence
(not a real signal, just model self-assessment) -
No user weighting
(opaque, gameable, and hard to defend) -
No market or price data
(keeps the signal orthogonal and interpretable)
If we ever add these, it will be because we can clearly justify the trade-off.
This algorithm is deliberately conservative. It is easier to add complexity later than to remove it once the system starts lying to you. If you change something here, the right question to ask is not:
“Does this improve performance?”
but:
“Does this make the signal more honest?”
That question should always win.