Welcome to Part 3 of our Whale Tracking series. In Part 2, we built a whale-discovery system that identifies the top trading wallets. Now, we're taking it a step further by implementing real-time monitoring and automated trading signals based on whale behaviour patterns. Let’s get started.
Prerequisites:
Completed Part 2 (or have the whale discovery code ready)
Basic understanding of WebSockets.
Note: We'll be making several changes to the Part 2 code to support real-time features. Don't worry, we'll walk through each change step by step.
Let's get started!
This is the same as what we have in part 2 of this guide.
import { GoldRushClient, StreamingChain } from "@covalenthq/client-sdk";
import WebSocket from "ws";
import * as dotenv from "dotenv";
dotenv.config();
(global as any).WebSocket = WebSocketWhat this does:
Imports the Covalent GoldRush client for API access.
Imports WebSocket for real-time connections.
Sets up WebSocket globally for our Node.js environment.
interface ContractMetadata {
contract_address: string;
contract_name: string;
contract_ticker_symbol: string;
contract_decimals: number;
}
interface WalletData {
token: string;
address: string;
volume: number;
transactions_count: number;
pnl_realized_usd: number;
balance: number;
balance_pretty: string;
pnl_unrealized_usd: number;
contract_metadata: ContractMetadata;
}
interface QueryResponse {
data?: {
upnlForToken: WalletData[];
};
error?: boolean;
error_message?: string;
error_code?: number;
}
interface TradingSignal {
whale_address: string;
signal: 'BUY' | 'SELL' | 'HOLD';
pattern: string;
confidence: number;
reasoning: string;
timestamp: string;
}
interface TransactionAlert {
type: 'LARGE_TRANSFER' | 'WHALE_MOVEMENT' | 'CONTRACT_INTERACTION' | 'PATTERN_MATCH';
severity: 'LOW' | 'MEDIUM' | 'HIGH';
message: string;
transaction: any;
whale_data?: WalletData;
}
This ensures type safety when working with the API responses.
Provides autocomplete and error checking in your IDE.
Defines automated trading signals generated by the system.
Defines alerts for significant whale transactions.
const API_KEY = process.env.COVALENT_API_KEY;
const client = new GoldRushClient(
API_KEY,
{},
{
onConnecting: () => console.log(" Connecting to Covalent service..."),
onOpened: () => console.log(" Connected to Covalent service!"),
onClosed: () => console.log(" Disconnected from Covalent service"),
onError: (error) => console.error(" Streaming error:", error),
}
);Replace the API key with your Covalent API key.
Event handlers provide connection status feedback.
The client manages WebSocket connections automatically.
class WhaleMonitoringSystem {
private discoveredWhales: Map<string, WalletData[]> = new Map();
private activeMonitors: Map<string, () => void> = new Map();
private tradingSignals: TradingSignal[] = [];discoveredWhales: Storage of whale addresses per token.
activeMonitors: Real-time tracking sessions across blockchain.
tradingSignals: Accumulated alpha signals for pattern analysis.
async discoverWhales(
chainName: string,
tokenAddress: string,
tokenSymbol?: string
): Promise<WalletData[] | null> {
console.log(`\n${"=".repeat(80)}`);
console.log(`Discovering Top Trading Wallets`);
console.log(`Chain: ${chainName}`);
console.log(`Token: ${tokenAddress}`);
if (tokenSymbol) console.log(`Symbol: ${tokenSymbol}`);
console.log(`${"=".repeat(80)}\n`);
return new Promise((resolve) => {
let hasResponded = false;
const unsubscribe = client.StreamingService.rawQuery(
`query {
upnlForToken(
chain_name: ${chainName}
token_address: "${tokenAddress}"
) {
token
address
volume
transactions_count
pnl_realized_usd
balance
balance_pretty
pnl_unrealized_usd
contract_metadata {
contract_address
contract_name
contract_ticker_symbol
contract_decimals
}
}
}`,
{},
{
next: (response: QueryResponse) => {
if (hasResponded) return;
hasResponded = true;
if (response.error) {
console.error(`API Error: ${response.error_message}`);
unsubscribe();
resolve(null);
return;
}
const wallets = response.data?.upnlForToken;
if (!wallets || wallets.length === 0) {
console.log("No trading data available for this token\n");
unsubscribe();
resolve(null);
return;
}
console.log(`Found ${wallets.length} trading wallets\n`);
this.discoveredWhales.set(tokenAddress, wallets);
this.displayWhaleSummary(wallets, tokenSymbol);
unsubscribe();
resolve(wallets);
},
error: (error) => {
if (hasResponded) return;
hasResponded = true;
console.error("Error fetching whales:", error);
unsubscribe();
resolve(null);
},
complete: () => {
if (!hasResponded) {
console.log("Query completed without response");
resolve(null);
}
}
}
);
setTimeout(() => {
if (!hasResponded) {
console.log("Query timeout after 30 seconds");
unsubscribe();
resolve(null);
}
}, 30000);
});
}This function executes blockchain queries to identify and profile our top trading whales.
Establishes a secure connection to the blockchain.
upnlForToken Query: Leverages Covalent's unrealized P&L endpoint for 30-day trading data.
Whale Profiling: Extracts volume, P&L, balances, and trading patterns.
Timeout: 30-second fail-safe to prevent hanging queries.
Storage: Caches whale data for real-time monitoring.
startRealTimeMonitoring(chain: StreamingChain = StreamingChain.BASE_MAINNET) {
const allWhaleAddresses: string[] = [];
this.discoveredWhales.forEach((wallets) => {
wallets.slice(0, 10).forEach(wallet => {
allWhaleAddresses.push(wallet.address);
});
});
if (allWhaleAddresses.length === 0) {
console.log("No whales discovered to monitor");
return;
}
console.log(`\nStarting real-time monitoring for ${allWhaleAddresses.length} whale addresses...`);
console.log(`\nMonitored Whale Addresses:`);
allWhaleAddresses.forEach((addr, i) => {
const whaleData = this.findWhaleData(addr);
const symbol = whaleData?.contract_metadata.contract_ticker_symbol || 'Unknown';
console.log(` ${i + 1}. ${addr.substring(0, 10)}...${addr.substring(addr.length - 6)} (${symbol})`);
});
console.log('');
const unsubscribe = client.StreamingService.subscribeToWalletActivity(
{
chain_name: chain,
wallet_addresses: allWhaleAddresses,
},
{
next: (data: any) => {
if (Array.isArray(data)) {
data.forEach(tx => this.processWhaleTransaction(tx));
} else if (data?.tx_hash) {
this.processWhaleTransaction(data);
}
},
error: (error: any) => {
console.error("Monitoring error:", error);
},
}
);
this.activeMonitors.set(chain, unsubscribe);
console.log(`Real-time monitoring active for chain: ${chain}\n`);
}Targeted Monitoring: Tracks the top 10 whales per discovered token.
Portfolio Overview: Displays all monitored addresses with token associations.
Live Subscription: Establishes a real-time WebSocket connection to the blockchain.
Instant Processing: Routes incoming transactions to the intelligence engine.
private processWhaleTransaction(tx: any): void {
// Only process if sender OR receiver is a monitored whale
const fromWhale = this.findWhaleData(tx.from_address);
const toWhale = this.findWhaleData(tx.to_address);
if (!fromWhale && !toWhale) {
return;
}
const alerts = this.analyzeWhaleTransaction(tx, fromWhale, toWhale);
// Only show transactions with alerts
if (alerts.length > 0) {
this.displayTransaction(tx);
alerts.forEach(alert => {
this.displayAlert(alert);
const signal = this.generateSignalFromAlert(alert);
if (signal) {
this.tradingSignals.push(signal);
this.displayTradingSignal(signal);
}
});
console.log('');
}
}This filters and processes blockchain transactions to extract meaningful whale intelligence.
Pattern Analysis: Deep examination of whale-involved transactions.
Alert Generation: Creates categorized alerts based on behavior patterns.
Signal Creation: Converts alerts into actionable trading signals.
Intelligence Storage: Archives signals for historical analysis.
private analyzeWhaleTransaction(tx: any, fromWhale?: WalletData, toWhale?: WalletData): TransactionAlert[] {
const alerts: TransactionAlert[] = [];
const value = parseFloat(tx.value || 0);
// ONLY create SELL signal if FROM is a monitored whale
if (tx.decoded_type === 'TRANSFER' && fromWhale && value > 1e10) {
alerts.push({
type: 'LARGE_TRANSFER',
severity: 'HIGH',
message: `WHALE TRANSFER OUT: ${this.formatValue(tx.value)}`,
transaction: tx,
whale_data: fromWhale
});
}
if (tx.decoded_type === 'TRANSFER' && toWhale && fromWhale && value > 1e10) {
alerts.push({
type: 'WHALE_MOVEMENT',
severity: 'HIGH',
message: `WHALE TRANSFER IN: ${this.formatValue(tx.value)}`,
transaction: tx,
whale_data: toWhale
});
}
if (tx.logs && tx.logs.length > 30 && (fromWhale || toWhale)) {
alerts.push({
type: 'CONTRACT_INTERACTION',
severity: 'MEDIUM',
message: `Major contract interaction: ${tx.logs.length} events`,
transaction: tx,
whale_data: fromWhale || toWhale
});
}
return alerts;
}Pattern recognition that identifies meaningful whale movements.
Distribution Detection: Large outgoing transfers with potential selling pressure.
Accumulation Detection: Whale-to-whale transfers are a potential buying opportunity.
DeFi Activity: This monitors multi-event transactions.
private findWhaleData(address: string): WalletData | undefined {
for (const [_, wallets] of this.discoveredWhales.entries()) {
const whale = wallets.find(w => w.address.toLowerCase() === address.toLowerCase());
if (whale) return whale;
}
return undefined;
}
private generateSignalFromAlert(alert: TransactionAlert): TradingSignal | null {
if (!alert.whale_data) return null;
let signal: 'BUY' | 'SELL' | 'HOLD' = 'HOLD';
let pattern = 'MONITORING';
let confidence = 0.6;
let reasoning = '';
const tx = alert.transaction;
// Handle incoming transfers to whales (accumulation)
if (alert.type === 'WHALE_MOVEMENT') {
signal = 'BUY';
pattern = 'WHALE_ACCUMULATION';
confidence = 0.75;
reasoning = `Whale receiving ${alert.whale_data.contract_metadata.contract_ticker_symbol}: ${this.formatValue(tx.value)} incoming`;
}
// Handle outgoing transfers from whales (distribution)
else if (alert.type === 'LARGE_TRANSFER' && tx.decoded_type === 'TRANSFER') {
signal = 'SELL';
pattern = 'WHALE_DISTRIBUTION';
confidence = 0.7;
reasoning = `Whale distributing ${alert.whale_data.contract_metadata.contract_ticker_symbol}: ${this.formatValue(tx.value)} moved out`;
}
// Only return signals with meaningful reasoning
if (!reasoning) return null;
return {
whale_address: alert.whale_data.address,
signal,
pattern,
confidence,
reasoning,
timestamp: new Date().toISOString()
};
}Transforms detected patterns into quantified trading signals with confidence scoring.
private displayWhaleSummary(wallets: WalletData[], tokenSymbol?: string): void {
const totalVolume = wallets.reduce((sum, w) => sum + w.volume, 0);
const totalTransactions = wallets.reduce((sum, w) => sum + w.transactions_count, 0);
const totalRealizedPnL = wallets.reduce((sum, w) => sum + w.pnl_realized_usd, 0);
console.log("Summary Statistics:");
console.log(` Total Volume: ${totalVolume.toLocaleString()} tokens`);
console.log(` Total Transactions: ${totalTransactions.toLocaleString()}`);
console.log(` Total Realized P&L: $${totalRealizedPnL.toLocaleString()}`);
console.log(`\n${"-".repeat(80)}\n`);
const displayCount = Math.min(wallets.length, 5);
console.log(`Top ${displayCount} Whales:\n`);
wallets.slice(0, displayCount).forEach((wallet, index) => {
const shortAddress = `${wallet.address.slice(0, 8)}...${wallet.address.slice(-6)}`;
const symbol = wallet.contract_metadata.contract_ticker_symbol || tokenSymbol || "Unknown";
console.log(`${index + 1}. ${shortAddress}`);
console.log(` Balance: ${parseFloat(wallet.balance_pretty).toLocaleString()} ${symbol}`);
console.log(` Volume: ${wallet.volume.toLocaleString()} ${symbol}`);
console.log(` Realized P&L: $${wallet.pnl_realized_usd.toLocaleString()}`);
});
console.log(`\n${"-".repeat(80)}`);
console.log("These whales are now being monitored in real-time");
console.log(`${"-".repeat(80)}\n`);
}
private formatValue(value: any): string {
if (!value && value !== 0) return '0';
const num = parseFloat(value);
if (isNaN(num)) return String(value);
if (num >= 1e18) return `${(num / 1e18).toFixed(2)} ETH`;
if (num >= 1e9) return `${(num / 1e9).toFixed(2)}B`;
if (num >= 1e6) return `${(num / 1e6).toFixed(2)}M`;
if (num >= 1e3) return `${(num / 1e3).toFixed(2)}K`;
return num.toLocaleString();
}
getRecentSignals(count: number = 5): TradingSignal[] {
return this.tradingSignals.slice(-count).reverse();
}
stopAllMonitoring(): void {
this.activeMonitors.forEach((unsubscribe, chain) => {
unsubscribe();
console.log(`Stopped monitoring on ${chain}`);
});
this.activeMonitors.clear();
}
}Presents complex blockchain intelligence in clear, actionable formats.
Whale Identification: Instant recognition of tracked whales.
Severity Indicators: Quick assessment of alert importance.
Confidence Scoring: Quantified the reliability of each signal using a confidence score.
const TOKEN_EXAMPLES = {
BASE_MAINNET: {
DEGEN: {
address: "0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed",
symbol: "DEGEN"
},
AXR: {
address: "0x58Db197E91Bc8Cf1587F75850683e4bd0730e6BF",
symbol: "AXR"
}
},
};Pre-configured token watchlist for immediate intelligence gathering. For this guide, we'll use the AXR and Degen tokens as examples, but you can substitute any tokens you prefer.
const main = async (): Promise<void> => {
console.log("Starting Advanced Whale Monitoring System\n");
const monitor = new WhaleMonitoringSystem();
try {
console.log("Phase 1: Whale Discovery\n");
await monitor.discoverWhales(
"BASE_MAINNET",
TOKEN_EXAMPLES.BASE_MAINNET.DEGEN.address,
TOKEN_EXAMPLES.BASE_MAINNET.DEGEN.symbol
);
await monitor.discoverWhales(
"BASE_MAINNET",
TOKEN_EXAMPLES.BASE_MAINNET.AXR.address,
TOKEN_EXAMPLES.BASE_MAINNET.AXR.symbol
);
console.log("\nPhase 2: Starting Real-time Monitoring\n");
monitor.startRealTimeMonitoring(StreamingChain.BASE_MAINNET);
const monitoringDuration = 10 * 60 * 1000; // 10 minutes
console.log(`Monitoring will run for ${monitoringDuration / 60000} minutes...`);
console.log("Press Ctrl+C to stop early\n");
const signalInterval = setInterval(() => {
const recentSignals = monitor.getRecentSignals(5);
if (recentSignals.length > 0) {
console.log(`\n${"=".repeat(80)}`);
console.log(`Trading Signals Summary (last 5):`);
console.log(`${"=".repeat(80)}`);
// Count signal types
const buys = recentSignals.filter(s => s.signal === 'BUY').length;
const sells = recentSignals.filter(s => s.signal === 'SELL').length;
const holds = recentSignals.filter(s => s.signal === 'HOLD').length;
console.log(`BUY Signals: ${buys} | SELL Signals: ${sells} | HOLD Signals: ${holds}\n`);
recentSignals.forEach((signal, idx) => {
const signalType = signal.signal === 'BUY' ? '[BUY]' : signal.signal === 'SELL' ? '[SELL]' : '[HOLD]';
console.log(`${idx + 1}. ${signalType} ${signal.pattern} (${(signal.confidence * 100).toFixed(0)}%)`);
console.log(` ${signal.reasoning}`);
console.log(` Whale: ${signal.whale_address.substring(0, 10)}...${signal.whale_address.substring(signal.whale_address.length - 6)}\n`);
});
}
}, 60000);
setTimeout(async () => {
clearInterval(signalInterval);
console.log(`\n${"=".repeat(80)}`);
console.log("Monitoring Session Complete");
console.log(`${"=".repeat(80)}`);
const allSignals = monitor.getRecentSignals(20);
if (allSignals.length > 0) {
console.log(`\nGenerated ${allSignals.length} trading signals total:\n`);
allSignals.forEach(signal => {
console.log(` ${signal.signal.padEnd(4)} | ${signal.pattern.padEnd(20)} | ${signal.whale_address.substring(0, 16)}...`);
});
}
monitor.stopAllMonitoring();
await client.StreamingService.disconnect();
process.exit(0);
}, monitoringDuration);
process.on('SIGINT', async () => {
clearInterval(signalInterval);
console.log(`\nStopping monitoring...`);
const signals = monitor.getRecentSignals(10);
if (signals.length > 0) {
console.log(`\nFinal Trading Signals (last 10):\n`);
signals.forEach(signal => {
console.log(` ${signal.signal} - ${signal.pattern} - ${signal.reasoning}`);
});
}
monitor.stopAllMonitoring();
await client.StreamingService.disconnect();
process.exit(0);
});
} catch (error) {
console.error("Fatal error:", error);
monitor.stopAllMonitoring();
await client.StreamingService.disconnect();
process.exit(1);
}
};
if (require.main === module) {
main().catch(console.error);
}
export { WhaleMonitoringSystem, TOKEN_EXAMPLES };Activation: Secure startup with connection validation.
Intelligence Gathering: Whale discovery across multiple tokens.
Live Activation: Real-time monitoring establishment.
Live Alpha Dashboard: Periodic signal summaries with statistics.
Open your terminal and run:
npx ts-node whale-tracker.tsYou should see output similar to this:

In this tutorial, we built a real-time tracker to monitor cryptocurrency "whales" wallets holding massive amounts of tokens. We configured it to track 20 specific whale addresses on the Base network and successfully caught serious action in real time. Our tracker alerted us that one major AXR whale began offloading over 4.3 million tokens, triggering a high-confidence SELL signal. This guide helps you to spot these major market moves early and make informed decisions, all built using the GoldRush Streaming API.
Happy coding!