agentsclimarketplace

Stock data processing

Skill cxcscmu/SkillLearnBench/skills/b4-skill-creator-claude-haiku-4-5/stock-data-visualization/stock-data-processing

Load, parse, and transform stock market data from CSV files for D3 visualization. Use this skill when working with financial CSV data, handling missing values (ETFs often lack market cap/country data), formatting market capitalization as human-readable strings (1.64T), and preparing data for both visualization and table display. Essential for stock dashboards, portfolio analytics, and financial data pipelines.From its SKILL.md

Install
npx -y skills add cxcscmu/SkillLearnBench --skill stock-data-processing

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

4.4 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it

Stock Data Processing for D3 Visualization

This skill covers loading CSV data and preparing it for stock market visualizations.

CSV Data Structure

Typical stock-descriptions CSV has columns:

Ticker,Name,Sector,Market Cap (Billions),Country,Website
AAPL,Apple Inc.,Technology,2800,USA,apple.com
ETF,ETF Name,ETF Sector,,,

Important: ETF rows have empty marketCap, Country, Website fields.

Loading CSV with d3-dsv

D3 v6 includes CSV parsing via d3.csv():

d3.csv("data/stock-descriptions.csv").then(rawData => {
  const data = rawData.map(d => ({
    ticker: d.Ticker,
    name: d.Name,
    sector: d.Sector || "Unknown",
    marketCapBillions: d["Market Cap (Billions)"] ? +d["Market Cap (Billions)"] : null,
    country: d.Country || null,
    website: d.Website || null,
    isETF: !d["Market Cap (Billions)"] // Detect ETFs by missing market cap
  }));

  // Continue with data
});

Formatting Market Cap

Convert billions to human-readable format (1.64T, 350B, etc.):

function formatMarketCap(billionValue) {
  if (!billionValue) return "N/A";

  const value = billionValue;
  if (value >= 1000) {
    return (value / 1000).toFixed(2) + "T";
  } else if (value >= 1) {
    return value.toFixed(2) + "B";
  } else {
    return (value * 1000).toFixed(0) + "M";
  }
}

Example: formatMarketCap(2800) → "2.80T"

Handling Individual Stock Data

If loading price history from separate files in indiv-stock/:

async function loadStockPrices(ticker) {
  try {
    const prices = await d3.csv(`data/indiv-stock/${ticker}.csv`);
    return prices.map(d => ({
      date: new Date(d.Date),
      close: +d.Close,
      volume: +d.Volume
    }));
  } catch {
    return null; // File doesn't exist
  }
}

Data Validation

function validateStockData(data) {
  const issues = [];

  // Check required fields
  if (!data.ticker || !data.name || !data.sector) {
    issues.push("Missing required fields");
  }

  // Check numeric validity
  if (data.marketCapBillions && data.marketCapBillions <= 0) {
    issues.push("Invalid market cap (negative or zero)");
  }

  // Check for duplicates
  // (Implement if using data from multiple sources)

  return issues.length === 0;
}

Grouping by Sector

const sectorGroups = d3.group(data, d => d.sector);
const sectors = Array.from(sectorGroups.keys()).sort();

Or for iteration:

const sectorCounts = d3.rollup(
  data,
  v => v.length,
  d => d.sector
);

Handling Edge Cases

  1. Missing Market Cap (ETFs):

    • Don't exclude from visualization
    • Use default/uniform sizing
    • Skip tooltips or mark as "N/A"
    • Don't show in sorted market cap rankings
  2. Duplicate Tickers:

    • Keep first occurrence
    • Log warning for duplicates
  3. Special Characters:

    • Tickers safe for CSS class names via: ticker.toLowerCase().replace(/[^a-z0-9]/g, '')
    • Names safe for HTML via: d3.html or escaped strings
  4. Missing Sector:

    • Default to "Other"
    • Ensure sector exists in color scale domain

Data Pipeline Example

async function loadStockData() {
  const raw = await d3.csv("data/stock-descriptions.csv");

  const data = raw
    .map(d => ({
      ticker: d.Ticker.trim(),
      name: d.Name.trim(),
      sector: d.Sector.trim() || "Other",
      marketCapBillions: d["Market Cap (Billions)"] ? +d["Market Cap (Billions)"] : null,
      country: d.Country.trim() || null,
      website: d.Website.trim() || null,
      isETF: !d["Market Cap (Billions)"]
    }))
    .filter(d => validateStockData(d));

  return data;
}

Testing Checklist

  • CSV loads without errors
  • All required fields present and non-null
  • Market cap formatting produces correct units (T/B/M)
  • ETFs correctly identified (no market cap = ETF)
  • Sector grouping works correctly
  • No duplicate tickers
  • Numeric conversions work (no NaN values)

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.