OG Advisory GroupOG Market Desk

OG Score Methodology

How the OG Market Desk Score is calculated — a multi-factor approach to market sentiment analysis.

Overview

The OG Score is a composite sentiment indicator ranging from -100 (extreme fear/bearish) to +100 (extreme greed/bullish). It combines 7 weighted factors that each independently assess a different dimension of market sentiment. When a factor lacks sufficient data, its weight is automatically redistributed to the remaining active factors.

-100
Extreme Fear
0
Neutral
+100
Extreme Greed

Factor Breakdown

Price Momentum

20%

Blends today's daily move with the year-to-date trend so a single bad day doesn't fully overwhelm a strong YTD performer (and vice versa). Falls back to daily-only when YTD data is unavailable.

Formula
0.4 × clamp(daily% × 20) + 0.6 × clamp(YTD% × 2)
Example
+2% today and +30% YTD → ~+44
Bullish:> +10
Bearish:< -10
Neutral:Between

Volume Signal

15%

Direction-aware volume conviction. Stocks compare against Yahoo Finance's rolling average volume; BTC compares against a 30-day rolling average from CoinGecko (with a $30B fallback constant if the chart is unavailable). The volume ratio magnitude is multiplied by the sign of today's price move, so high volume confirms whichever direction price went.

Formula
clamp((volume / avgVolume - 1) × 100) × sign(change%)
Example
2× avg volume on a green day → +100. 2× avg on a red day → −100 (capitulation).
Bullish:> +15
Bearish:< -15
Neutral:Between

News Sentiment

18%

Per-article sentiment averaged across articles tagged to the ticker. Articles come from LunarCrush's per-ticker news feed (which aggregates publishers including CoinDesk, Decrypt, Bloomberg, Bitcoin.com News, PR Newswire, Business Wire, and others) and arrive pre-classified with a positive/neutral/negative label and a −100…+100 score. SEC EDGAR filings (10-K, 10-Q, 8-K, Form 4, S-1, S-3, 13D/G) are pulled separately and shown in their own tab — they are excluded from this factor so the article count matches what the News tab shows. Articles with a score of 0 are excluded from the average so they don't dilute the signal toward zero. Every article that comes through is also persisted to Supabase with a 30-day rolling retention, so the page keeps showing news for a ticker even when LunarCrush rotates an article off its recent-feed window.

Formula
mean(article.score) over graded articles only
Example
5 graded articles averaging +60 → +60
Bullish:> +15
Bearish:< -15
Neutral:Between

Social Sentiment

15%

Aggregated X and Reddit chatter for the ticker over the last 24 hours, sourced from LunarCrush. Each platform contributes an engagement-weighted positive ratio; the combined score is the volume-weighted blend of the two. The raw 0..100 score gets noisy at low post counts — a single viral tweet can swing a 20-post topic to extreme readings — so we apply Bayesian shrinkage toward neutral by the ratio n / (n + 50), where n is total X+Reddit posts. At n=50 the score is shrunk by 50%; by n=500 only ~9%; high-volume names (1k+ posts) are essentially unchanged. For low-rank topics where LunarCrush's aggregated per-source score comes back empty, we derive the source score from the underlying positive/neutral/negative post counts so freshly-listed small caps still register. Reads 0 when there is no meaningful chatter in the window; the 15% weight then redistributes to the remaining active factors.

Formula
((X score × X interactions + Reddit score × Reddit interactions) ÷ total interactions, mapped to ±100) × n / (n + 50)
Example
FLD with 31 posts and a raw +84 lean → shrunk to ~+32. MARA with 1,400 posts and +58 → essentially unchanged.
Bullish:> +10
Bearish:< -10
Neutral:Between

52-Week Position

10%

Where the current price sits within its 52-week high/low range. Stocks near highs trend bullish; stocks near lows trend bearish.

Formula
((price − low) / (high − low) − 0.5) × 200
Example
At 52W high → +100. At midpoint → 0. At 52W low → −100.
Bullish:> 70% of range
Bearish:< 30% of range
Neutral:Between

Sector Momentum

12%

Average daily price change of every other stock in the same sector (excluding the ticker itself). Captures sector-wide trends and rotation. Uses a wide ±10% band so a single rough day (already reflected in Price Momentum) does not crush this factor to the floor.

Formula
mean(peer change%) × 10, clamped to ±100
Example
Sector peers averaging +3% → +30; a -6% sector day → -60
Bullish:Sector avg > +0.5%
Bearish:Sector avg < -0.5%
Neutral:Between

Sentiment Momentum

10%

Week-over-week rate of change in the OG Score. Compares the live pre-momentum score (the six other factors) against the same score recorded ~7 days ago in Supabase's daily snapshot table. Shows "Building momentum data..." until a 7-day-old base-score snapshot exists for the ticker.

Formula
(currentBaseScore − baseScoreOneWeekAgo) × 3
Example
Base score improved by +15 points vs. one week ago → +45
Bullish:Delta > +5
Bearish:Delta < -5
Neutral:Stable

Weight Distribution

20%
15%
18%
15%
10%
12%
10%
Price Momentum
Volume Signal
News Sentiment
Social Sentiment
52-Week Position
Sector Momentum
Sentiment Momentum

BTC OG Score

Bitcoin gets its own four-factor model on the dashboard tracker — distinct from the per-stock score above because the relevant signals are different (no sector peers, no analyst coverage, but a market-wide Fear & Greed reading).

Fear & Greed

30%

The Crypto Fear & Greed Index (alternative.me), 0–100, normalized to the OG Score range. Captures broad sentiment regime — extreme fear is contrarian-bullish; extreme greed is contrarian-bearish, but the score reflects the level itself.

Formula
(fearGreedIndex − 50) × 2
Example
Index at 75 (Greed) → +50. Index at 20 (Fear) → −60.

News Sentiment

25%

Average sentiment across articles in LunarCrush's curated "bitcoin" topic feed — explicitly Bitcoin-about journalism, not company news that happens to mention BTC. Wire-service press releases (PR Newswire, Business Wire, GlobeNewswire, ACCESS Newswire) are filtered out for this score because LunarCrush's bitcoin topic pulls in miner/treasury company PR that mentions Bitcoin in passing — useful on those per-ticker pages, but noise here. The BTC feed reads live from LunarCrush only (no Supabase carry-over), so if an article rotates off the topic feed it disappears from the score immediately. Sentiment scores arrive pre-graded from LunarCrush's 1–5 scale (1 = bearish, 5 = bullish) and are mapped to ±100.

Formula
mean(article.score) over graded articles from the LunarCrush bitcoin topic, wire-service sources excluded
Example
14 graded articles averaging +8 → +8

Price Momentum

25%

24-hour BTC price change normalized to the OG Score range.

Formula
clamp(change24h × 20)
Example
+5% in 24 h → +100. −2.5% → −50.

Volume

20%

Direction-aware volume conviction for BTC, mirroring the stock volume factor. Baseline is a rolling 30-day average computed from CoinGecko's market_chart payload (excluding the in-progress hour). Falls back to a $30B constant if the chart is unavailable. The ratio × direction yields four interpretations: heavy buying conviction, heavy selling pressure, thin rally, or light selling.

Formula
clamp((volume24h / avg30d − 1) × 100) × sign(change24h)
Example
132% of 30-day avg on a down day → −32 ("heavy selling pressure")
30%
25%
25%
20%

Additional Signals

Divergence Detection

When the OG Score disagrees with price action by a significant margin, a divergence is flagged. Bullish divergence: price is down >2% but sentiment is above +20. Bearish divergence: price is up >2% but sentiment is below -20.

Signal Strength

Measures factor agreement. Strong: 60%+ of active factors agree on direction. Moderate: 40-60% agreement. Weak: below 40%.

Missing Data Handling

When a factor lacks data (e.g., no news articles found), its weight is set to zero and the remaining factors' weights are automatically normalized to sum to 100%. The "Active Factors" badge on each stock shows how many of the 7 factors had available data.

Data Pipeline & Resilience

Sources

Quotes and fundamentals come from Yahoo Finance (with Finnhub and Polygon as fallbacks). News and social signals for per-ticker pages and the BTC tracker come from LunarCrush. Filings are pulled from SEC EDGAR. Crypto price, dominance, and 30-day volume baseline come from CoinGecko; the market-wide Fear & Greed reading comes from alternative.me. Analyst coverage is from Yahoo.

Refresh Cadence

Stock quotes refresh every ~2 minutes through the dashboard cache. News and social data refresh once daily via a warming cron at 21:30 UTC that throttles LunarCrush calls to stay under the tier rate limit; the BTC tracker payload is cached for 5 minutes and the per-ticker page payload for 2 minutes. The dashboard also records a daily sentiment snapshot to Supabase for momentum comparisons.

Persistence & Fallback

Per-ticker news, social snapshots, and spike alerts are persisted to Supabase with a 30-day rolling retention. When the live source is unreachable (rate limited, outage, or a freshly- listed ticker before its first refresh), the page falls back to the most recent persisted snapshot so it never renders blank. The BTC tracker is the one exception — it reads strictly from LunarCrush's live bitcoin topic feed with no carry-over, so rotated-off articles disappear from the score immediately.

Caching

All cached values live in Upstash Redis (shared across serverless function instances) with an in-memory fallback for the current process. A circuit breaker trips after a LunarCrush 429 and holds reads off the upstream for 10 minutes while serving from cache and Supabase, so a single rate-limit burst doesn't cascade.

Spike Alerts

Each daily refresh runs a backend delta-detection pass against the rolling Supabase history. For every tracked metric (mentions, creators, engagements, social dominance) the current value is compared against 7-day, 30-day, and 90-day averages; a ±50% deviation fires an alert that shows on the ticker's social panel. Tickers with fewer than three prior snapshots in a window are skipped — not enough baseline to trust.

Technical Notes

  • All factor scores are clamped to the −100 to +100 range before weighting.
  • The weighted average is computed only over active factors (weight > 0), with weights renormalized.
  • Sentiment momentum compares the live pre-momentum (6-factor) score against the ticker's base score from the closest Supabase daily snapshot on or before 7 days ago, falling back to a building-data placeholder until such a snapshot exists. The snapshot row is read once per hour and cached, so a bulk dashboard pass costs one Supabase read regardless of ticker count.
  • The preliminary score (6 factors, excluding momentum) is computed first, then sentiment momentum is derived from it to avoid circular dependency.
  • Volume signal is direction-aware: the volume ratio magnitude is multiplied by the sign of the price change, so high volume confirms the prevailing price direction. The ticker breakdown shows a quadrant-specific caption (heavy buying conviction / heavy selling pressure / thin rally / light selling).
  • BTC's volume baseline is a rolling 30-day average from CoinGecko's market_chart endpoint, computed in the same call that powers the 7-day sparkline. A $30B constant is used as a fallback if the chart payload is unavailable.
  • Social sentiment uses Bayesian shrinkage with a pseudo-count of 50 toward neutral, so low-volume tickers don't hit extreme scores on a handful of posts.
  • For low-rank LunarCrush topics where the pre-aggregated per-source score is empty, the source score is derived from the underlying positive/neutral/negative post counts.
  • Per-stock OG Score responses are cached for 2 minutes; the BTC tracker payload is cached for 5 minutes.

Methodology Changelog

current: v1.3.0

Every change to how the score is computed is recorded here, in plain language, when it ships. Entries marked “backfill” were reconstructed from commit history when versioning was introduced. How well does the score line up with what happens next? See the validation research →

  • 2026-06-12v1.3.0

    Methodology versioning introduced; daily snapshots now record the version that produced them. No factor math changed.

    Why: So score history can be segmented by methodology era and the validation page never mixes eras silently.

  • 2026-06-02v1.2.1 (backfill)

    Per-factor scores and weights began being recorded with each daily snapshot. No factor math changed.

    Why: Instrumentation for measuring each factor’s predictive value before considering any weight changes.

  • 2026-06-01v1.2.1 (backfill)

    Sentiment Momentum factor repaired after a week of missing baselines (May 26 – June 1).

    Why: A data gap, not a design change — noted so history from that week is read with care.

  • 2026-05-26v1.2.0 (backfill)

    Six-factor base score began being recorded alongside the total, enabling week-over-week Sentiment Momentum.

    Why: Momentum needs a stable baseline that excludes itself.

  • 2026-05-07v1.1.0 (backfill)

    Social Sentiment factor enabled, backed by LunarCrush (X + Reddit), with low-volume scores shrunk toward neutral.

    Why: A handful of posts shouldn’t swing a stock’s score; shrinkage keeps thin social data honest.

  • 2026-02-19v1.0.0 (backfill)

    Daily score snapshots began. Seven-factor model with the published weights.

    Why: Start of the recorded history used everywhere on this site.