DEV Community

Danilo Jamaal
Danilo Jamaal

Posted on

Build an AltRank Monitor to Catch Social Momentum Early

Social momentum often shifts before mainstream attention catches up. Track it in real-time.


Quick Facts

Metric Value
Build Time 20 minutes
Difficulty Beginner to Intermediate
Stack Node.js + LunarCrush API
Output CLI monitoring tool

Table of Contents

  1. Why AltRank Matters
  2. How AltRank Works
  3. Prerequisites
  4. Project Setup
  5. Building the AltRank Monitor
  6. Adding Alert Logic
  7. Running the Monitor
  8. ROI Analysis
  9. Troubleshooting
  10. AI Extension Prompts
  11. Resources

Why AltRank Matters

Social metrics can shift before mainstream attention catches up. The challenge is detecting these shifts early rather than reacting to viral posts after the fact.

That's where AltRank comes in.

AltRank is LunarCrush's proprietary ranking system that combines:

  • Social volume changes
  • Engagement velocity
  • Sentiment shifts
  • Correlation patterns

The key insight: AltRank measures relative social performance across all tracked assets. A rapidly improving AltRank means an asset is gaining social mindshare compared to everything else - which can be an early indicator worth watching.

How to Interpret AltRank

AltRank Range Meaning
1-50 Top tier social performance
51-200 Strong relative performance
201-500 Moderate attention
500+ Below average social presence

Lower is better. An asset moving from rank 500 to rank 100 is gaining significant relative social momentum.


How AltRank Works

The Problem vs The Solution

Without LunarCrush:

  • Manually refresh Twitter every 5 minutes
  • Miss signals while sleeping
  • React to pumps after they're over
  • Rely on influencer call-outs (usually late)

With This Build:

  • Automated monitoring every 2 minutes
  • Instant alerts when AltRank improves significantly
  • Catch the move before the market catches on
  • Data-driven entries, no FOMO

The AltRank Signal Pattern

Lower AltRank = Better. Here's the key pattern to watch:

AltRank Signal Strength:
- 50%+ improvement in 1 hour → STRONG signal
- 30-50% improvement → MODERATE signal
- 10-30% improvement → WEAK signal
Enter fullscreen mode Exit fullscreen mode

Our CLI will track these improvements in real-time.


Prerequisites

Before we start, you'll need:

  • Node.js v18+ installed
  • Basic JavaScript/Node.js knowledge
  • LunarCrush API key (Get one here - use code JAMAALBUILDS for 15% off any plan)

Project Setup

Create your project:

mkdir altrank-monitor && cd altrank-monitor
npm init -y
npm install dotenv chalk
Enter fullscreen mode Exit fullscreen mode

Project Structure

altrank-monitor/
├── .env                 # API key
├── .env.example         # Template
├── package.json
├── src/
│   ├── index.js         # Main entry
│   ├── lunarcrush.js    # API client
│   ├── monitor.js       # Monitoring logic
│   └── alerts.js        # Alert system
└── README.md
Enter fullscreen mode Exit fullscreen mode

Environment Setup

📄 .env.example

LUNARCRUSH_API_KEY=your_api_key_here
WATCHLIST=SOL,ETH,BTC,DOGE,PEPE
POLL_INTERVAL_MS=120000
ALTRANK_THRESHOLD=30
Enter fullscreen mode Exit fullscreen mode

📄 .env

LUNARCRUSH_API_KEY=your_actual_key
WATCHLIST=SOL,ETH,BTC,DOGE,PEPE
POLL_INTERVAL_MS=120000
ALTRANK_THRESHOLD=30
Enter fullscreen mode Exit fullscreen mode

Building the AltRank Monitor

Step 1: LunarCrush API Client

📄 src/lunarcrush.js

import 'dotenv/config';

const API_KEY = process.env.LUNARCRUSH_API_KEY;
const BASE_URL = 'https://lunarcrush.com/api4/public';

/**
 * Fetch coin data with AltRank from LunarCrush
 * @param {string[]} symbols - Array of coin symbols
 * @returns {Promise<Object[]>} Coin data with AltRank
 */
export async function fetchCoinData(symbols) {
  const response = await fetch(
    `${BASE_URL}/coins/list/v1?sort=alt_rank&limit=100`,
    {
      headers: {
        'Authorization': `Bearer ${API_KEY}`
      }
    }
  );

  if (!response.ok) {
    throw new Error(`API Error: ${response.status} ${response.statusText}`);
  }

  const data = await response.json();

  // Filter to our watchlist
  const watchlistData = data.data.filter(coin =>
    symbols.includes(coin.symbol.toUpperCase())
  );

  return watchlistData.map(coin => ({
    symbol: coin.symbol,
    name: coin.name,
    altRank: coin.alt_rank,
    galaxyScore: coin.galaxy_score,
    price: coin.price,
    priceChange24h: coin.percent_change_24h,
    socialVolume: coin.social_volume,
    socialDominance: coin.social_dominance
  }));
}

/**
 * Fetch historical AltRank data for a coin
 * @param {string} symbol - Coin symbol
 * @returns {Promise<Object[]>} Time series data
 */
export async function fetchAltRankHistory(symbol) {
  const response = await fetch(
    `${BASE_URL}/coins/${symbol.toLowerCase()}/time-series/v2?interval=1d`,
    {
      headers: {
        'Authorization': `Bearer ${API_KEY}`
      }
    }
  );

  if (!response.ok) {
    throw new Error(`API Error: ${response.status}`);
  }

  const data = await response.json();
  return data.data || [];
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Monitoring Logic

📄 src/monitor.js

import chalk from 'chalk';
import { fetchCoinData } from './lunarcrush.js';
import { checkAlerts, formatAlert } from './alerts.js';

// Store previous readings for comparison
const previousReadings = new Map();

/**
 * Run a single monitoring cycle
 * @param {string[]} watchlist - Coins to monitor
 * @param {number} threshold - AltRank improvement threshold
 */
export async function runMonitorCycle(watchlist, threshold) {
  console.log(chalk.gray(`\n[${new Date().toLocaleTimeString()}] Fetching data...`));

  try {
    const coinData = await fetchCoinData(watchlist);

    console.log(chalk.bold('\n📊 AltRank Monitor'));
    console.log(chalk.gray(''.repeat(70)));
    console.log(
      chalk.gray('Symbol'.padEnd(8)),
      chalk.gray('AltRank'.padEnd(10)),
      chalk.gray('Change'.padEnd(12)),
      chalk.gray('Galaxy'.padEnd(8)),
      chalk.gray('Price'.padEnd(12)),
      chalk.gray('Signal')
    );
    console.log(chalk.gray(''.repeat(70)));

    const alerts = [];

    for (const coin of coinData) {
      const prev = previousReadings.get(coin.symbol);
      const change = prev ? ((prev.altRank - coin.altRank) / prev.altRank * 100) : 0;

      // Format change indicator
      let changeStr = '';
      let changeColor = chalk.gray;

      if (prev && change !== 0) {
        if (change > 0) {
          changeStr = `▲ ${change.toFixed(1)}%`;
          changeColor = change >= threshold ? chalk.green.bold : chalk.green;
        } else {
          changeStr = `▼ ${Math.abs(change).toFixed(1)}%`;
          changeColor = chalk.red;
        }
      }

      // Determine signal strength
      let signal = '';
      if (change >= 50) {
        signal = chalk.bgGreen.black(' STRONG ');
      } else if (change >= 30) {
        signal = chalk.bgYellow.black(' MODERATE ');
      } else if (change >= 10) {
        signal = chalk.yellow(' WEAK ');
      }

      // Check for alerts
      const alert = checkAlerts(coin, prev, threshold);
      if (alert) {
        alerts.push({ coin, alert, change });
      }

      console.log(
        chalk.white(coin.symbol.padEnd(8)),
        chalk.cyan(String(coin.altRank).padEnd(10)),
        changeColor(changeStr.padEnd(12)),
        chalk.magenta(String(coin.galaxyScore || '').padEnd(8)),
        chalk.yellow(`$${coin.price?.toFixed(2) || ''}`.padEnd(12)),
        signal
      );

      // Store for next comparison
      previousReadings.set(coin.symbol, {
        altRank: coin.altRank,
        price: coin.price,
        timestamp: Date.now()
      });
    }

    console.log(chalk.gray(''.repeat(70)));

    // Display any alerts
    if (alerts.length > 0) {
      console.log(chalk.bgRed.white.bold('\n🚨 ALERTS'));
      for (const { coin, alert, change } of alerts) {
        console.log(formatAlert(coin, alert, change));
      }
    }

    console.log(chalk.gray(`\nNext check in ${process.env.POLL_INTERVAL_MS / 1000}s...`));

  } catch (error) {
    console.error(chalk.red(`Error: ${error.message}`));
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Alert System

📄 src/alerts.js

import chalk from 'chalk';

/**
 * Check if coin triggers an alert
 * @param {Object} current - Current coin data
 * @param {Object} previous - Previous reading
 * @param {number} threshold - Alert threshold percentage
 * @returns {string|null} Alert type or null
 */
export function checkAlerts(current, previous, threshold) {
  if (!previous) return null;

  const altRankImprovement = ((previous.altRank - current.altRank) / previous.altRank) * 100;

  // Strong signal: AltRank improved by threshold or more
  if (altRankImprovement >= threshold) {
    if (altRankImprovement >= 50) {
      return 'STRONG_SIGNAL';
    } else if (altRankImprovement >= 30) {
      return 'MODERATE_SIGNAL';
    }
    return 'WEAK_SIGNAL';
  }

  // Galaxy Score spike (crossed above 70)
  if (current.galaxyScore >= 70 && previous.galaxyScore && previous.galaxyScore < 70) {
    return 'GALAXY_SPIKE';
  }

  return null;
}

/**
 * Format an alert for display
 * @param {Object} coin - Coin data
 * @param {string} alertType - Type of alert
 * @param {number} change - AltRank change percentage
 * @returns {string} Formatted alert string
 */
export function formatAlert(coin, alertType, change) {
  const timestamp = new Date().toLocaleTimeString();

  const alerts = {
    STRONG_SIGNAL: `
${chalk.bgGreen.black.bold(' STRONG SIGNAL ')} ${chalk.white(coin.symbol)}
${chalk.gray('├─')} AltRank improved ${chalk.green.bold(`${change.toFixed(1)}%`)} (${coin.altRank})
${chalk.gray('├─')} Galaxy Score: ${chalk.magenta(coin.galaxyScore)}
${chalk.gray('├─')} Price: ${chalk.yellow(`$${coin.price?.toFixed(2)}`)}
${chalk.gray('└─')} Time: ${timestamp}

${chalk.cyan('💡 Action: Consider entry. Social momentum building before CT catches on.')}
`,
    MODERATE_SIGNAL: `
${chalk.bgYellow.black.bold(' MODERATE SIGNAL ')} ${chalk.white(coin.symbol)}
${chalk.gray('├─')} AltRank improved ${chalk.yellow.bold(`${change.toFixed(1)}%`)} (${coin.altRank})
${chalk.gray('├─')} Galaxy Score: ${chalk.magenta(coin.galaxyScore)}
${chalk.gray('└─')} Time: ${timestamp}

${chalk.cyan('💡 Action: Add to watchlist. Monitor for continuation.')}
`,
    WEAK_SIGNAL: `
${chalk.yellow(' WEAK SIGNAL ')} ${coin.symbol} - AltRank +${change.toFixed(1)}%
`,
    GALAXY_SPIKE: `
${chalk.bgMagenta.white.bold(' GALAXY SPIKE ')} ${chalk.white(coin.symbol)}
${chalk.gray('├─')} Galaxy Score crossed 70: ${chalk.magenta.bold(coin.galaxyScore)}
${chalk.gray('└─')} Time: ${timestamp}

${chalk.cyan('💡 Action: High social health. Check for catalyst.')}
`
  };

  return alerts[alertType] || '';
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Main Entry Point

📄 src/index.js

import 'dotenv/config';
import chalk from 'chalk';
import { runMonitorCycle } from './monitor.js';

// Configuration
const WATCHLIST = process.env.WATCHLIST?.split(',') || ['SOL', 'ETH', 'BTC'];
const POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL_MS) || 120000;
const THRESHOLD = parseInt(process.env.ALTRANK_THRESHOLD) || 30;

console.log(chalk.bold.cyan(`
╔═══════════════════════════════════════════════════════════╗
║           🔍 AltRank Early Detection Monitor              ║
║                                                           ║
║  Catch pumps before Crypto Twitter catches on.            ║
║  Social precedes price. AltRank precedes social.          ║
╚═══════════════════════════════════════════════════════════╝
`));

console.log(chalk.gray('Configuration:'));
console.log(chalk.gray(`  Watchlist: ${WATCHLIST.join(', ')}`));
console.log(chalk.gray(`  Poll interval: ${POLL_INTERVAL / 1000}s`));
console.log(chalk.gray(`  Alert threshold: ${THRESHOLD}% AltRank improvement`));

// Run immediately, then on interval
runMonitorCycle(WATCHLIST, THRESHOLD);
setInterval(() => runMonitorCycle(WATCHLIST, THRESHOLD), POLL_INTERVAL);

// Handle graceful shutdown
process.on('SIGINT', () => {
  console.log(chalk.yellow('\n\nMonitor stopped. Happy trading! 🚀'));
  process.exit(0);
});
Enter fullscreen mode Exit fullscreen mode

Step 5: Package Configuration

📄 package.json

{
  "name": "altrank-monitor",
  "version": "1.0.0",
  "type": "module",
  "description": "AltRank early detection monitor - catch pumps before CT",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js"
  },
  "keywords": ["lunarcrush", "crypto", "altrank", "trading"],
  "license": "MIT",
  "dependencies": {
    "chalk": "^5.3.0",
    "dotenv": "^16.3.1"
  }
}
Enter fullscreen mode Exit fullscreen mode

Running the Monitor

npm start
Enter fullscreen mode Exit fullscreen mode

Expected Output

╔═══════════════════════════════════════════════════════════╗
║           🔍 AltRank Early Detection Monitor              ║
║                                                           ║
║  Catch pumps before Crypto Twitter catches on.            ║
║  Social precedes price. AltRank precedes social.          ║
╚═══════════════════════════════════════════════════════════╝

Configuration:
  Watchlist: SOL, ETH, BTC, DOGE, PEPE
  Poll interval: 120s
  Alert threshold: 30% AltRank improvement

[10:00:15 AM] Fetching data...

📊 AltRank Monitor
──────────────────────────────────────────────────────────────────────
Symbol   AltRank   Change       Galaxy   Price        Signal
──────────────────────────────────────────────────────────────────────
SOL      89        ▲ 42.3%      74       $108.40       MODERATE
ETH      12        ▲ 8.2%       82       $3,421.50
BTC      3         —            91       $97,234.00
DOGE     234       ▼ 12.1%      61       $0.42
PEPE     567       ▲ 15.8%      58       $0.0000021    WEAK
──────────────────────────────────────────────────────────────────────

🚨 ALERTS

 MODERATE SIGNAL  SOL
├─ AltRank improved 42.3% (89)
├─ Galaxy Score: 74
├─ Price: $108.40
└─ Time: 10:00:15 AM

💡 Action: Add to watchlist. Monitor for continuation.

Next check in 120s...
Enter fullscreen mode Exit fullscreen mode

ROI Analysis

The Math: Is This Worth Your Time?

Value of catching 1 pump early:

  • Average early entry edge: 5-15%
  • On a $1,000 position: $50-150 profit per trade
  • 2-4 signals per week = 8-16 per month

The real value: One good signal per month can cover cost for the API subscription. Everything else is profit.


Troubleshooting

Error Cause Solution
401 Unauthorized Invalid API key Check .env file, verify key at lunarcrush.com
429 Too Many Requests Rate limit hit Increase POLL_INTERVAL_MS, upgrade plan with JAMAALBUILDS
ENOTFOUND Network issue Check internet connection
No data for coin Coin not tracked Check if symbol exists in LunarCrush
Stale readings API lag Normal during low volume periods

AI Extension Prompts

Copy these into Claude or ChatGPT to extend the project:

Add Telegram Notifications

I have an AltRank monitor CLI built with Node.js. Help me add Telegram
notifications when a STRONG_SIGNAL alert fires. I want to:
1. Send a formatted message to my Telegram chat
2. Include the coin symbol, AltRank change, and current price
3. Use the node-telegram-bot-api package
Enter fullscreen mode Exit fullscreen mode

Add Historical Backtesting

I want to validate my AltRank signal strategy. Help me build a backtest
module that:
1. Fetches historical AltRank data from LunarCrush time-series
2. Identifies past signals using my threshold logic
3. Calculates what the returns would have been if I entered on each signal
4. Outputs a win rate and average return
Enter fullscreen mode Exit fullscreen mode

Add Multi-Timeframe Analysis

Enhance my AltRank monitor to track improvements across multiple timeframes:
1. 1-hour AltRank change
2. 4-hour AltRank change
3. 24-hour AltRank change
Weight the signals (1h = 50%, 4h = 30%, 24h = 20%) for a composite score.
Enter fullscreen mode Exit fullscreen mode

Tip: Paste your existing alerts.js and monitor.js files when using these prompts for best results.


Challenge: Extend This Project

EASY: Add support for tracking the top 20 coins by market cap automatically.

MEDIUM: Store all readings in SQLite and build a query to find the best-performing signals historically.

HARD: Combine AltRank with Galaxy Score momentum to create a composite "Pump Probability" score.

Share your solution with #LunarCrushBuilder on Twitter!


Resources


Drop a comment if you build this! Tag me at @jamaalbuilds and use #LunarCrushBuilder to get featured.


Built with LunarCrush API. Social intelligence for builders who ship.

Top comments (0)