Skip to main content

Overview

Fantasy Basketball Analytics provides multiple ways to analyze your team’s performance. This guide explains how to interpret each type of analysis and make informed decisions about your roster.

Category Rankings

How Rankings Work

Rankings are calculated by comparing all teams in your league for each statistical category:
dashboard.js
computeRanks: teams => {
  const ranks = Array.from({ length: teams.length }, () => ({}));
  
  CONFIG.COLS.forEach(([id, , dir]) => {
    const vals = teams.map((t, i) => ({ num: parseFloat(t.statMap[id]), i }));
    vals.sort((a, b) => {
      if (isNaN(a.num)) return 1;
      if (isNaN(b.num)) return -1;
      return dir === 'high' ? b.num - a.num : a.num - b.num;
    });
    
    let curr = 0, prev = null;
    vals.forEach(({ num, i }, idx) => {
      if (isNaN(num)) { 
        ranks[i][id] = '-'; 
        return;
      }
      if (prev === null || num !== prev) { 
        curr = idx + 1; 
        prev = num; 
      }
      ranks[i][id] = curr;
    });
  });
  
  return ranks;
}

Understanding Sort Direction

High Categories (higher is better):
  • Points (PTS)
  • Rebounds (REB)
  • Assists (AST)
  • Steals (ST)
  • Blocks (BLK)
  • 3-Pointers Made (3PTM)
  • Field Goal % (FG%)
  • Free Throw % (FT%)
  • Double-Doubles (DD)
Low Categories (lower is better):
  • Turnovers (TO)
For low categories, rank 1 means you have the fewest turnovers.

Ordinal Display

Rankings are shown as ordinals (1st, 2nd, 3rd, etc.):
dashboard.js
ordinal: n => {
  const s = ['th', 'st', 'nd', 'rd'], v = n % 100;
  return n + (s[(v - 20) % 10] || s[v] || s[0]);
}
When the “Show team ranks per category” checkbox is enabled, you’ll see superscript rankings next to each stat value.

Tied Rankings

When teams have identical values, they receive the same rank:
dashboard.js
if (prev === null || num !== prev) { 
  curr = idx + 1;  // New rank
  prev = num; 
}
ranks[i][id] = curr;  // Same rank if values match
Example: If two teams both average 105.2 points, they both get rank “3rd”, and the next team gets “5th”.

Win/Loss Comparisons

The Score Column

Each row shows a win-loss record against the selected team:
dashboard.js
recordVsUser: (baseTeam, oppTeam) => {
  let w = 0, l = 0;
  CONFIG.COLS.forEach(([id, , dir]) => {
    const u = parseFloat(baseTeam[id]);
    const o = parseFloat(oppTeam[id]);
    if (isNaN(u) || isNaN(o) || u === o) return;  // Skip ties
    (dir === 'high' ? u > o : u < o) ? w++ : l++;
  });
  return `${w} - ${l}`;
}

Reading Win/Loss Records

Example: “6 - 3” means:
  • You would win 6 categories against this opponent
  • You would lose 3 categories against this opponent
  • Ties are not counted in the record
In a 9-category league:
  • 6-3 = You win the matchup
  • 5-4 = You win the matchup
  • 4-5 = You lose the matchup
  • 5-5 would be a tie (rare, as percentages usually differ)

Color Coding

Cells are color-coded to show advantages:
dashboard.js
let cls = '';
if (raw !== '–' && baseTeam.statMap[id] !== '–') {
  const a = parseFloat(raw), b = parseFloat(baseTeam.statMap[id]);
  if (!isNaN(a) && !isNaN(b) && a !== b) {
    cls = (dir === 'high' ? b > a : b < a) ? 'better' : 'worse';
  }
}
  • Green/Better: Your team has the advantage in this category
  • Red/Worse: The opponent has the advantage
  • No color: Values are equal or missing

Category Strength Analysis

The dashboard automatically identifies your strongest and weakest categories:
dashboard.js
generateCategoryAnalysis: (teams, ranks, selectedTeamIndex = 0) => {
  const totalTeams = teams.length;
  const weakCategories = [];
  const strongCategories = [];
  const myRanks = ranks[selectedTeamIndex];

  CONFIG.COLS.forEach(([id, name, sortDir]) => {
    const rank = myRanks[id];
    if (rank && rank !== '-' && typeof rank === 'number') {
      const weakThreshold = Math.ceil(totalTeams * 0.7);    // Bottom 30%
      const strongThreshold = Math.ceil(totalTeams * 0.3);  // Top 30%
      
      if (rank >= weakThreshold) {
        weakCategories.push({ name, rank, id });
      } else if (rank <= strongThreshold) {
        strongCategories.push({ name, rank, id });
      }
    }
  });

  weakCategories.sort((a, b) => b.rank - a.rank);
  strongCategories.sort((a, b) => a.rank - b.rank);
  
  return { weakCategories, strongCategories, totalTeams };
}

Strength Thresholds

In a 12-team league:
  • Strong categories: Rank 1-4 (top 30%)
    • Elite production in these stats
    • Build your strategy around maintaining these advantages
  • Neutral categories: Rank 5-8 (middle 40%)
    • Average production
    • Opportunity for improvement
  • Weak categories: Rank 9-12 (bottom 30%)
    • Areas needing improvement
    • Consider punting or targeting in trades
The thresholds scale with league size.

Analysis Display

dashboard.js
formatAnalysisSummary: (analysis, mode = 'weekly') => {
  const { weakCategories, strongCategories, teamName } = analysis;
  let summary = `<div class="analysis-content">`;

  if (strongCategories.length > 0) {
    const strongList = strongCategories.slice(0, 3).map(cat => 
      `<strong>${cat.name}</strong> (${Utils.ordinal(cat.rank)})`
    ).join(', ');
    summary += `<div class="strength-summary">
      <span class="highlight positive">Strongest categories:</span> ${strongList}
    </div>`;
  }

  if (weakCategories.length > 0) {
    const weakList = weakCategories.slice(0, 3).map(cat => 
      `<strong>${cat.name}</strong> (${Utils.ordinal(cat.rank)})`
    ).join(', ');
    summary += `<div class="weakness-summary">
      <span class="highlight negative">Areas to improve:</span> ${weakList}
    </div>`;
  }

  return summary + `</div>`;
}
The analysis shows your top 3 strongest and weakest categories. Use this to identify punt strategies or trade targets.

Reading Trend Charts

Weekly Progression

Trends charts plot your team’s statistics week-by-week:
trends.js
weeklyStats: [
  { week: 1, stats: { '12': 105.2, '15': 45.3, ... } },
  { week: 2, stats: { '12': 108.7, '15': 43.1, ... } },
  // ...
]

Chart Interpretation

Upward trends (for high-better stats):
  • Improving performance over time
  • Recent acquisitions or player improvements
  • Positive momentum
Downward trends (for high-better stats):
  • Declining performance
  • Injuries or player slumps
  • Need for roster adjustments
Flat trends:
  • Consistent production
  • Stable roster
  • Predictable performance
Volatile trends (jagged lines):
  • Inconsistent matchups
  • Streaming players
  • Injury management

Percentage Statistics

Percentage stats are formatted specially:
dashboard.js
formatStatValue: (id, value) => {
  if (CONFIG.PERCENTAGE_STATS.includes(id)) {
    const num = parseFloat(value);
    if (isNaN(num)) return value;
    return num.toFixed(3).replace(/^0\./, '.');  // ".453" instead of "0.453"
  }
  return value;
}
Percentage stats display with 3 decimal places (e.g., “.453” for 45.3% shooting). Small changes (±.005) can significantly impact rankings.

Trade Impact Scores

Category Impact Calculation

The trade analyzer shows how each category would change:
trade_analyzer.js
const impactVal = aggregatedStats.impact[statId] || 0;
const isPercentage = statMapInfo.type === 'percentage';
const isLowGood = sortDir === 'low';

let impactClass = '';
const diffThreshold = isPercentage ? 0.0001 : 0.01;
if (Math.abs(impactVal) > diffThreshold) { 
  if ((isLowGood && impactVal < 0) || (!isLowGood && impactVal > 0)) {
    impactClass = 'team-improves';
  } else {
    impactClass = 'team-declines';
  }
}

Impact Display

Positive impacts (green):
  • “+2.5” points per game
  • “+.015” field goal percentage
  • Indicates category improvement
Negative impacts (red):
  • “-1.8” rebounds per game
  • “-.008” free throw percentage
  • Indicates category decline
Near-zero impacts:
  • “+0.1” or “-0.1”
  • Minimal effect on standings
  • Neutral trade from this category’s perspective

Ranking Context

The trade analyzer shows your current rank in each category:
trade_analyzer.js
const currentRank = rankingAnalysis.currentRanks[statId];
const rankDisplay = currentRank && currentRank !== '-' 
  ? Utils.ordinal(currentRank) 
  : 'N/A';

tableHTML += `<td style="text-align: center; font-weight: 600;">${rankDisplay}</td>`;
Focus on improving categories where you’re ranked 4th-7th. A small boost could move you from middle-of-the-pack to top-3.

Strategic Insights

The analyzer provides contextual advice:
trade_analyzer.js
generateStrategicInsightsHTML: (insights) => {
  if (insights.improvesWeakCategories) {
    html += `<div class="insight-item positive">
      <strong>Strengthens weak areas:</strong> 
      This trade improves ${insights.weakCategoriesImproved.join(', ')}
    </div>`;
  }
  
  if (insights.sacrificesStrengths) {
    html += `<div class="insight-item warning">
      <strong>Warning:</strong> 
      You're giving up advantages in ${insights.strongCategoriesLost.join(', ')}
    </div>`;
  }
}

Percentage vs Counting Stats

Counting Statistics

Stats like points, rebounds, assists that accumulate:
trade_analyzer.js
NBA_STAT_MAP: {
  '12': { nbaKey: 'PTS', type: 'counting', precision: 1 },
  '15': { nbaKey: 'REB', type: 'counting', precision: 1 },
  '16': { nbaKey: 'AST', type: 'counting', precision: 1 }
}
  • Additive: Trading a 25 PPG player for two 15 PPG players increases points
  • Volume matters: More games played = more stats
  • Linear impact: Doubling minutes roughly doubles stats

Percentage Statistics

Efficiency stats calculated from makes/attempts:
trade_analyzer.js
'5': { 
  type: 'percentage', 
  components: { made: 'FGM', attempted: 'FGA' }, 
  nbaKey: 'FG_PCT', 
  precision: 3 
}
  • Non-additive: Can’t simply add percentages together
  • Volume affects impact: High-volume shooters have bigger influence
  • Weighted by attempts: A .500 shooter on 10 FGA affects team % more than .500 on 2 FGA
When trading players that affect FG%, the impact calculation considers:
  1. Current team totals: Your team’s total FGM and FGA
  2. Players out: Subtract their FGM and FGA from your team
  3. Players in: Add their FGM and FGA to your team
  4. Recalculate: New FG% = New FGM / New FGA
This is why a high-efficiency, low-volume player (e.g., .600 on 3 FGA/game) might have less impact than a .480 shooter on 15 FGA/game.

Common Patterns

Punt Strategies

Identifying categories to intentionally lose:
  1. Look for categories where you’re ranked 9th or worse
  2. Check if improving them would require major roster overhaul
  3. Consider trading away players strong in those categories
  4. Focus resources on competitive categories
Example: If you’re 12th in FT%, trade high-FT% players for specialists in rebounds and blocks.

Balanced vs Specialist Teams

Balanced teams:
  • Ranks 3-7 across most categories
  • Few dominant strengths or glaring weaknesses
  • Competitive in every matchup
  • Harder to trade improve
Specialist teams:
  • Ranks 1-2 in several categories
  • Deliberately punt 2-3 categories
  • More volatile matchup results
  • Clearer trade targets

Week-to-Week Variance

Weekly results fluctuate based on:
  • Games played: More games = more counting stats
  • Opponent quality: Strength of schedule affects all stats
  • Rest days: Back-to-backs hurt efficiency
  • Injuries: Even one missing star swings results
Use season totals/averages for roster decisions. Don’t overreact to a single bad week.

Advanced Analysis Techniques

Opportunity Cost

When evaluating trades, consider:
Net value = (Players In - Players Out) + (Waiver Pickups - Roster Drops)
A 2-for-1 trade opens a roster spot for a waiver pickup, which adds hidden value.

Playoff Scheduling

Late-season trends matter more:
  • Check NBA schedules for playoff weeks
  • Teams with more games during playoffs have higher value
  • Back-to-backs in crucial weeks are risky

Strength of Schedule

Remaining matchups affect value:
  • Teams playing elite defenses will underperform
  • Teams with favorable schedules are undervalued
  • Consider opponent quality in trend analysis

Next Steps

Dashboard Navigation

Master the dashboard interface and controls

API Reference

Learn about the data behind the analytics

Build docs developers (and LLMs) love