feat: sentiment as a signed adjustment to the composite, not averaged in
Deploy / lint (push) Successful in 23s
Deploy / test (push) Successful in 54s
Deploy / deploy (push) Successful in 31s

Going from no sentiment to a bullish read used to be able to *lower* the composite:
sentiment was blended into the weighted average as an absolute level, so a bullish
75 diluted a ticker already scoring 78. That's backwards for a directional signal.

Now the non-sentiment dimensions form a re-normalized weighted-average base, and
sentiment is applied as a signed adjustment around neutral (50):

    composite = clamp(base + MAX_ADJ * (sentiment - 50) / 50)
    MAX_ADJ   = sentiment weight * 100   (default weight 0.10 → ±10)

Neutral leaves the base unchanged, bullish adds and bearish subtracts (scaled by
confidence, since a 50%-confidence call maps to 50 → no effect), and no sentiment
never penalises. Default sentiment weight 0.15 → 0.10; the weight now means "max ±
points." Composite breakdown exposes base_score/sentiment_score/sentiment_adjustment,
and the ScoreCard shows "Base 78 · sentiment +5.0" plus the per-dimension adjustment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 09:34:37 +02:00
parent 1566b84379
commit f61e11adea
5 changed files with 204 additions and 31 deletions
+6
View File
@@ -33,6 +33,12 @@ class CompositeBreakdownResponse(BaseModel):
missing_dimensions: list[str]
renormalized_weights: dict[str, float]
formula: str
# Sentiment is applied as a signed adjustment on top of the non-sentiment base
# rather than averaged in.
base_score: float | None = None
sentiment_score: float | None = None
sentiment_adjustment: float | None = None
max_sentiment_adjustment: float | None = None
class DimensionScoreResponse(BaseModel):