Model V0.1
The exact production inputs, formula, gates, components, and edge cases.
#Inputs and window
For every configured symbol, V0.1 reads the latest 20 persisted observations by default. Each contains bid, ask, midpoint, source timestamp, and receipt timestamp. The model uses the equal-weight series across the configured universe and, when configured, the benchmark series.
The score is withheld unless every required series reaches the full window. The oldest required source timestamp must be no more than 90 seconds old at evaluation. Halted quotes, invalid numeric values, and failed API responses never enter the series.
#Exact formula
assetMomentumPct = mean((lastMid - firstMid) / firstMid Γ 100)
benchmarkMomentumPct = benchmark window return, or 0 for basket models
relativeMovementPct = assetMomentumPct - benchmarkMomentumPct
consistency = aligned consecutive basket steps / 19
spreadQuality = 1 - clamp(averageSpreadBps / 50, 0, 1)
score = round(clamp(50
+ clamp(assetMomentumPct Γ 5, -15, 15)
+ clamp(relativeMovementPct Γ 8, -20, 20)
+ (consistency - 0.5) Γ 20
+ (spreadQuality - 0.5) Γ 10,
0, 100))#Stored components
| Component | Range / behavior | Source |
|---|---|---|
| momentumContribution | Clipped to -15β¦15 | Universe midpoint return |
| relativeContribution | Clipped to -20β¦20 | Universe minus benchmark |
| consistencyContribution | Approximately -10β¦10 | Direction-aligned steps |
| qualityContribution | -5β¦5 | Average bid/ask spread |
#Stance thresholds
Scores 0 through 33 classify UNDERWEIGHT. Scores 34 through 66 classify NEUTRAL. Scores 67 through 100 classify OVERWEIGHT. Boundary tests cover 33/34 and 66/67 exactly.
#Edge cases
A non-positive first midpoint produces zero percent change rather than division by zero, but upstream validation normally rejects non-positive midpoints. A flat basket assigns a positive total-direction fallback solely to make consistency deterministic. A missing required or benchmark series prevents output. Spread quality is clipped so an extremely wide spread cannot push the component below its documented bound.
#TypeScript reference
const result = scoreConviction(seriesBySymbol, benchmarkSymbol, {
requiredObservations: 20,
requiredSymbols: universe,
maxSourceAgeSeconds: 90,
now: Date.now(),
thresholds: { underweightMax: 33, overweightMin: 67 },
weights: { momentum: 5, relative: 8, consistency: 20, quality: 10 },
});