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
- Why AltRank Matters
- How AltRank Works
- Prerequisites
- Project Setup
- Building the AltRank Monitor
- Adding Alert Logic
- Running the Monitor
- ROI Analysis
- Troubleshooting
- AI Extension Prompts
- 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
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
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
Environment Setup
📄 .env.example
LUNARCRUSH_API_KEY=your_api_key_here
WATCHLIST=SOL,ETH,BTC,DOGE,PEPE
POLL_INTERVAL_MS=120000
ALTRANK_THRESHOLD=30
📄 .env
LUNARCRUSH_API_KEY=your_actual_key
WATCHLIST=SOL,ETH,BTC,DOGE,PEPE
POLL_INTERVAL_MS=120000
ALTRANK_THRESHOLD=30
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 || [];
}
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}`));
}
}
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] || '';
}
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);
});
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"
}
}
Running the Monitor
npm start
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...
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
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
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.
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
- LunarCrush API Docs - Full endpoint reference
- LunarCrush Pricing - Use code JAMAALBUILDS for 15% off the Builder plan
- @jamaalbuilds - Follow for more builds
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)