
Whale Tracking
- 32 installs
- 610 repo stars
- Updated June 26, 2026
- alsk1992/cloddsbot
whale-tracking is a skill that monitors large whale trades on Polymarket and crypto chains to identify market-moving activity.
About
whale-tracking monitors large trades and positions on Polymarket and across six crypto chains to spot market-moving activity. A developer uses it to stream whale trades over WebSocket, track specific wallets, and rank top or profitable whales. It integrates Polymarket, Birdeye (Solana), and Alchemy (EVM chains).
- Real-time WebSocket monitoring of whale trades on Polymarket
- On-chain whale tracking across Solana, Ethereum, Polygon, Arbitrum, Base, and Optimism
- Tracks specific wallets and surfaces top and profitable whales
Whale Tracking by the numbers
- 32 all-time installs (skills.sh)
- Ranked #656 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
whale-tracking capabilities & compatibility
- Capabilities
- trading · data analysis
- Use cases
- trading · data analysis
- Pricing
- Bring your own API key
What whale-tracking says it does
Monitor large trades and positions on Polymarket and crypto chains to identify market-moving activity.
minTradeSize: 10000, // $10k+ trades
npx skills add https://github.com/alsk1992/cloddsbot --skill whale-trackingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 32 |
|---|---|
| repo stars | ★ 610 |
| Last updated | June 26, 2026 |
| Repository | alsk1992/cloddsbot ↗ |
What it does
Monitor whale trades on Polymarket and crypto chains to spot market-moving activity.
Who is it for?
Traders who want real-time alerts on large Polymarket and on-chain whale trades.
When should I use this skill?
You want to stream or query whale trades and positions on Polymarket or a crypto chain.
By the numbers
- 6 crypto chains: Solana, Ethereum, Polygon, Arbitrum, Base, Optimism
- default thresholds: $10k trades, $50k positions
Files
Whale Tracking - Complete API Reference
Monitor large trades and positions on Polymarket and crypto chains to identify market-moving activity.
Supported Platforms
Prediction Markets
- Polymarket - WebSocket real-time trade monitoring
Crypto Chains
| Chain | Provider | Features |
|---|---|---|
| Solana | Birdeye WebSocket | Token transfers, swaps, NFTs |
| Ethereum | Alchemy WebSocket | ERC-20, ETH transfers |
| Polygon | Alchemy WebSocket | MATIC, tokens |
| Arbitrum | Alchemy WebSocket | L2 activity |
| Base | Alchemy WebSocket | Coinbase L2 |
| Optimism | Alchemy WebSocket | OP ecosystem |
---
Chat Commands
General
/whale # Active whale alerts summary
/whale start # Start whale monitoring
/whale stop # Stop monitoring
/whale config # Tracking configuration
/whale list # List tracked walletsTracking Wallets
/whale track <address> # Follow specific wallet
/whale untrack <address> # Stop following wallet
/whale watch <address> [--chain c] # Track an address (same as track)
/whale unwatch <address> # Stop tracking (same as untrack)Polymarket Whale Activity
/whale polymarket [n] # Recent Polymarket whale trades
/whale polymarket market <id> # Whale activity for a market
/whale activity <market> # Whale activity for market
/whale recent [n] [--min-size N] # Last N whale trades
/whale top [n] # Top Polymarket whales
/whale profitable [wr%] [min-n] # Profitable whales
/whale profile <address> # Whale profile + positions
/whale positions [market-id] # Active whale positionsCrypto Whale Tracking
/whale crypto [chain] [n] # On-chain whale txs
/whale top crypto [chain] [n] # Top on-chain whalesNote: /whales also works as an alias for /whale.
---
TypeScript API Reference
Polymarket Whale Tracker
import { createWhaleTracker, isWhaleAddress, getMarketWhaleActivity } from 'clodds/feeds/polymarket/whale-tracker';
// Create tracker
const tracker = createWhaleTracker({
minTradeSize: 10000, // $10k+ trades
minPositionSize: 50000, // $50k+ positions
enableRealtime: true, // WebSocket streaming
});
// Event handlers
tracker.on('trade', (trade) => {
console.log(`🐋 Whale ${trade.side} $${trade.usdValue.toLocaleString()}`);
console.log(` Market: ${trade.marketQuestion}`);
console.log(` Address: ${trade.address}`);
console.log(` Price: ${trade.price}`);
});
tracker.on('positionOpened', (position) => {
console.log(`📈 New whale position`);
console.log(` Address: ${position.address}`);
console.log(` Market: ${position.market}`);
console.log(` Size: $${position.size.toLocaleString()}`);
});
tracker.on('positionClosed', (position) => {
console.log(`📉 Whale exited position`);
console.log(` P&L: $${position.pnl.toLocaleString()}`);
});
// Start tracking
await tracker.start();
// Track specific wallet
await tracker.trackAddress('0x1234...');
await tracker.untrackAddress('0x1234...');
// Get tracked addresses
const tracked = tracker.getTrackedAddresses();
// Stop tracking
await tracker.stop();Check Whale Status
// Check if address is a whale
const isWhale = await isWhaleAddress('0x1234...', 100000); // $100k min volume
// Get whale activity for a market
const activity = await getMarketWhaleActivity('market-slug');
console.log(`Total whale volume: $${activity.totalVolume.toLocaleString()}`);
console.log(`Whale trades: ${activity.tradeCount}`);
console.log(`Net whale sentiment: ${activity.sentiment}`); // 'bullish' | 'bearish' | 'neutral'
console.log(`Top whales:`);
for (const whale of activity.topWhales) {
console.log(` ${whale.address}: $${whale.volume.toLocaleString()}`);
}Top Traders
// Get top traders
const topTraders = await tracker.getTopTraders({
period: '7d', // 24h, 7d, 30d, all
limit: 10,
minVolume: 50000,
minWinRate: 0.5,
});
for (const trader of topTraders) {
console.log(`${trader.rank}. ${trader.address}`);
console.log(` Volume: $${trader.volume.toLocaleString()}`);
console.log(` Win rate: ${(trader.winRate * 100).toFixed(1)}%`);
console.log(` P&L: $${trader.pnl.toLocaleString()}`);
}---
Crypto Whale Tracker
import { createCryptoWhaleTracker } from 'clodds/feeds/crypto/whale-tracker';
// Create multi-chain tracker
const cryptoTracker = createCryptoWhaleTracker({
chains: ['solana', 'ethereum', 'polygon', 'arbitrum', 'base', 'optimism'],
thresholds: {
solana: 10000, // $10k+ on Solana
ethereum: 50000, // $50k+ on ETH
polygon: 5000, // $5k+ on Polygon
arbitrum: 10000, // $10k+ on Arbitrum
base: 10000, // $10k+ on Base
optimism: 10000, // $10k+ on Optimism
},
// API keys
birdeyeApiKey: process.env.BIRDEYE_API_KEY,
alchemyApiKey: process.env.ALCHEMY_API_KEY,
});
// Event handlers
cryptoTracker.on('transaction', (tx) => {
console.log(`🐋 ${tx.chain.toUpperCase()}`);
console.log(` Type: ${tx.type}`); // 'transfer', 'swap', 'nft', 'stake'
console.log(` Amount: $${tx.usdValue.toLocaleString()}`);
console.log(` From: ${tx.from}`);
console.log(` To: ${tx.to}`);
console.log(` Token: ${tx.token}`);
console.log(` TX: ${tx.hash}`);
});
cryptoTracker.on('largeTransfer', (tx) => {
console.log(`🚨 LARGE TRANSFER: $${tx.usdValue.toLocaleString()}`);
});
// Start tracking
await cryptoTracker.start();
// Watch specific wallets
await cryptoTracker.watchWallet('solana', 'ABC123...');
await cryptoTracker.watchWallet('ethereum', '0x1234...');
// Unwatch
await cryptoTracker.unwatchWallet('solana', 'ABC123...');
// Get recent whale transactions
const recent = await cryptoTracker.getRecentTransactions('solana', {
limit: 20,
minValue: 50000,
});
// Get top whales by chain
const topSolana = await cryptoTracker.getTopWhales('solana', 10);
const topEth = await cryptoTracker.getTopWhales('ethereum', 10);
// Stop
await cryptoTracker.stop();---
Alert Configuration
Polymarket Alerts
// Configure alerts
tracker.setAlertConfig({
minTradeSize: 25000, // Alert on $25k+ trades
alertChannels: ['telegram', 'discord'],
soundEnabled: true,
markets: ['politics', 'crypto'], // Filter by category
});Crypto Alerts
// Configure per-chain alerts
cryptoTracker.setAlertConfig({
solana: {
minValue: 50000,
tokens: ['SOL', 'JUP', 'BONK'],
},
ethereum: {
minValue: 100000,
tokens: ['ETH', 'USDC', 'PEPE'],
},
});---
Data Export
// Export whale data to CSV
await tracker.exportTrades({
format: 'csv',
path: './whale-trades.csv',
period: '30d',
});
// Export top traders
await tracker.exportTopTraders({
format: 'json',
path: './top-traders.json',
limit: 100,
});---
Best Practices
1. Set reasonable thresholds - Start with $10k for active markets 2. Focus on specific markets - Filter by category to reduce noise 3. Track known smart money - Follow wallets with proven track records 4. Combine with copy trading - Use whale data to inform copy decisions 5. Monitor sentiment shifts - Watch for net whale direction changes 6. Check position sizes - Large positions signal conviction
/**
* Whale Tracking CLI Skill
*
* Commands:
* /whale (or /whales) - View active whale alerts
* /whale start - Start whale monitoring
* /whale stop - Stop whale monitoring
* /whale track <address> - Follow specific wallet
* /whale untrack <address> - Stop following wallet
* /whale recent [n] - Recent whale trades
* /whale activity <market> - Whale activity for market
* /whale polymarket - Polymarket whale activity
* /whale crypto [chain] - On-chain whale movements
* /whale watch <address> - Watch specific address
* /whale config - Tracking configuration
*/
async function execute(args: string): Promise<string> {
const parts = args.trim().split(/\s+/);
const cmd = parts[0]?.toLowerCase() || 'summary';
try {
const polyMod = await import('../../../feeds/polymarket/whale-tracker');
const cryptoMod = await import('../../../feeds/crypto/whale-tracker');
const polyTracker = polyMod.createWhaleTracker();
const cryptoTracker = cryptoMod.createCryptoWhaleTracker();
switch (cmd) {
case 'summary':
case '': {
// Show combined whale activity across Polymarket and on-chain
const polyTrades = polyTracker.getRecentTrades(10);
const cryptoTxs = cryptoTracker.getRecentTransactions(undefined, 10);
const cryptoStats = cryptoTracker.getStats();
const polyState = polyTracker.getConnectionState();
let output = '**Whale Activity Summary**\n\n';
output += `Polymarket WS: ${polyState}\n`;
output += `Crypto chains: ${cryptoStats.chains.join(', ') || 'none'}\n`;
output += `Watched wallets: ${cryptoStats.watchedWallets}\n`;
output += `Transactions tracked: ${cryptoStats.transactionsTracked}\n`;
output += `Alerts generated: ${cryptoStats.alertsGenerated}\n\n`;
if (polyTrades.length > 0) {
output += `**Recent Polymarket Whale Trades** (${polyTrades.length})\n\n`;
for (const t of polyTrades) {
const time = t.timestamp.toLocaleTimeString();
output += ` [${time}] ${t.side} ${t.outcome} $${t.usdValue.toLocaleString(undefined, { maximumFractionDigits: 0 })} @ ${t.price.toFixed(2)}`;
if (t.marketQuestion) output += ` - ${t.marketQuestion.slice(0, 60)}`;
output += '\n';
}
} else {
output += 'No recent Polymarket whale trades.\n';
}
if (cryptoTxs.length > 0) {
output += `\n**Recent On-Chain Whale Txs** (${cryptoTxs.length})\n\n`;
for (const tx of cryptoTxs) {
const time = tx.timestamp.toLocaleTimeString();
const value = tx.amountUsd > 0 ? `$${tx.amountUsd.toLocaleString(undefined, { maximumFractionDigits: 0 })}` : `${tx.amount.toLocaleString()} ${tx.tokenSymbol}`;
output += ` [${time}] ${tx.chain} ${tx.type} ${value} ${tx.tokenSymbol}`;
output += `\n ${tx.from.slice(0, 10)}... -> ${tx.to.slice(0, 10)}...\n`;
}
} else {
output += '\nNo recent on-chain whale transactions.\n';
}
return output;
}
case 'polymarket':
case 'poly': {
const subCmd = parts[1]?.toLowerCase();
if (subCmd === 'market' && parts[2]) {
// Get whale activity for a specific market
const marketId = parts[2];
const activity = await polyMod.getMarketWhaleActivity(marketId);
let output = `**Market Whale Activity**\n\n`;
output += `Total whale volume: $${activity.totalWhaleVolume.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n`;
output += `Buy volume: $${activity.buyVolume.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n`;
output += `Sell volume: $${activity.sellVolume.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n\n`;
if (activity.topBuyers.length > 0) {
output += `Top buyers:\n`;
for (const addr of activity.topBuyers.slice(0, 5)) {
output += ` \`${addr}\`\n`;
}
}
if (activity.topSellers.length > 0) {
output += `\nTop sellers:\n`;
for (const addr of activity.topSellers.slice(0, 5)) {
output += ` \`${addr}\`\n`;
}
}
return output;
}
// Default: recent Polymarket whale trades
const parsedLimit = parseInt(parts[1] || '20', 10);
const limit = isNaN(parsedLimit) || parsedLimit <= 0 ? 20 : parsedLimit;
const trades = polyTracker.getRecentTrades(limit);
const state = polyTracker.getConnectionState();
let output = `**Polymarket Whale Trades** (WS: ${state})\n\n`;
if (trades.length === 0) {
output += 'No whale trades recorded yet. Tracker may need to be started.\n';
output += 'Use `/whales polymarket market <id>` to check a specific market.\n';
return output;
}
for (const t of trades) {
const time = t.timestamp.toLocaleTimeString();
const maker = t.maker !== 'unknown' ? t.maker.slice(0, 8) + '...' : '?';
const taker = t.taker !== 'unknown' ? t.taker.slice(0, 8) + '...' : '?';
output += ` [${time}] ${t.side} ${t.outcome} $${t.usdValue.toLocaleString(undefined, { maximumFractionDigits: 0 })} @ ${t.price.toFixed(2)}\n`;
output += ` Maker: ${maker} | Taker: ${taker}`;
if (t.marketQuestion) output += `\n Market: ${t.marketQuestion.slice(0, 70)}`;
output += '\n';
}
return output;
}
case 'crypto':
case 'onchain': {
const chain = parts[1]?.toLowerCase();
const validChains = ['solana', 'ethereum', 'polygon', 'arbitrum', 'base', 'optimism'];
const filterChain = chain && validChains.includes(chain) ? chain as any : undefined;
const parsedLimit = parseInt(parts[2] || '20', 10);
const limit = isNaN(parsedLimit) || parsedLimit <= 0 ? 20 : parsedLimit;
const txs = cryptoTracker.getRecentTransactions(filterChain, limit);
const stats = cryptoTracker.getStats();
let output = `**On-Chain Whale Activity**`;
if (filterChain) output += ` (${filterChain})`;
output += `\n\nChains: ${stats.chains.join(', ')}\n`;
output += `Running: ${stats.running ? 'Yes' : 'No'}\n`;
output += `Tracked txs: ${stats.transactionsTracked}\n`;
output += `Alerts: ${stats.alertsGenerated}\n\n`;
if (txs.length === 0) {
output += 'No whale transactions recorded yet.\n';
output += 'Supported chains: ' + validChains.join(', ') + '\n';
return output;
}
for (const tx of txs) {
const time = tx.timestamp.toLocaleTimeString();
const value = tx.amountUsd > 0 ? `$${tx.amountUsd.toLocaleString(undefined, { maximumFractionDigits: 0 })}` : `${tx.amount.toLocaleString()} ${tx.tokenSymbol}`;
output += ` [${time}] ${tx.chain} ${tx.type} ${value}\n`;
output += ` ${tx.from.slice(0, 10)}... -> ${tx.to.slice(0, 10)}...\n`;
if (tx.swapDetails) {
output += ` Swap: ${tx.swapDetails.amountIn} ${tx.swapDetails.tokenInSymbol} -> ${tx.swapDetails.amountOut} ${tx.swapDetails.tokenOutSymbol}`;
if (tx.swapDetails.dex) output += ` (${tx.swapDetails.dex})`;
output += '\n';
}
}
return output;
}
case 'watch': {
if (!parts[1]) return 'Usage: /whales watch <address> [--chain <chain>]\n\nTracks an address across Polymarket and on-chain.';
const address = parts[1];
const chainIdx = parts.indexOf('--chain');
const chain = chainIdx >= 0 ? parts[chainIdx + 1] : undefined;
const validChains = ['solana', 'ethereum', 'polygon', 'arbitrum', 'base', 'optimism'];
// Track on Polymarket
polyTracker.trackAddress(address);
// Track on-chain
if (chain && validChains.includes(chain)) {
cryptoTracker.watchWallet(address, chain as any);
} else {
// Watch on all configured chains
cryptoTracker.watchWallet(address);
}
let output = `**Now Watching** \`${address}\`\n\n`;
output += `Polymarket: tracking\n`;
output += `On-chain: ${chain || 'all configured chains'}\n`;
// Check if it's a known whale on Polymarket
const isWhale = await polyMod.isWhaleAddress(address);
if (isWhale) {
output += `\nThis address is a known Polymarket whale (>$100k volume).\n`;
}
// Try to get existing profile
const profile = polyTracker.getWhaleProfile(address);
if (profile) {
output += `\nExisting profile found:\n`;
output += ` Total value: $${profile.totalValue.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n`;
output += ` Win rate: ${profile.winRate.toFixed(1)}%\n`;
output += ` Positions: ${profile.positions.length}\n`;
output += ` Recent trades: ${profile.recentTrades.length}\n`;
}
return output;
}
case 'unwatch': {
if (!parts[1]) return 'Usage: /whales unwatch <address>';
const address = parts[1];
polyTracker.untrackAddress(address);
cryptoTracker.unwatchWallet(address);
return `Stopped watching \`${address}\` on all platforms.`;
}
case 'top': {
const subCmd = parts[1]?.toLowerCase();
if (subCmd === 'crypto' || subCmd === 'onchain') {
const chain = (parts[2] || 'solana') as any;
const parsedLimit = parseInt(parts[3] || '10', 10);
const limit = isNaN(parsedLimit) || parsedLimit <= 0 ? 10 : parsedLimit;
const topWhales = await cryptoTracker.getTopWhales(chain, limit);
if (topWhales.length === 0) {
return `No tracked whales on ${chain} yet. Watch wallets with \`/whales watch <address> --chain ${chain}\`.`;
}
let output = `**Top Crypto Whales** (${chain})\n\n`;
for (let i = 0; i < topWhales.length; i++) {
const w = topWhales[i];
output += ` ${i + 1}. \`${w.address.slice(0, 12)}...\`\n`;
output += ` Value: $${w.totalValueUsd.toLocaleString(undefined, { maximumFractionDigits: 0 })}`;
if (w.labels.length > 0) output += ` [${w.labels.join(', ')}]`;
output += '\n';
if (w.holdings.length > 0) {
const top3 = w.holdings.slice(0, 3);
output += ` Top: ${top3.map(h => `${h.symbol} $${h.valueUsd.toLocaleString(undefined, { maximumFractionDigits: 0 })}`).join(', ')}\n`;
}
}
return output;
}
// Default: Polymarket top whales
const parsedLimit = parseInt(parts[1] || '10', 10);
const limit = isNaN(parsedLimit) || parsedLimit <= 0 ? 10 : parsedLimit;
const topWhales = polyTracker.getTopWhales(limit);
if (topWhales.length === 0) {
return 'No whale profiles tracked yet. Tracker may need to be started.';
}
let output = `**Top Polymarket Whales** (by portfolio value)\n\n`;
for (let i = 0; i < topWhales.length; i++) {
const w = topWhales[i];
output += ` ${i + 1}. \`${w.address.slice(0, 12)}...\`\n`;
output += ` Value: $${w.totalValue.toLocaleString(undefined, { maximumFractionDigits: 0 })}`;
output += ` | WR: ${w.winRate.toFixed(1)}%`;
output += ` | Positions: ${w.positions.length}`;
output += ` | Trades: ${w.recentTrades.length}\n`;
}
return output;
}
case 'profitable': {
const parsedWR = parseFloat(parts[1] || '55');
const minWR = (isNaN(parsedWR) ? 55 : parsedWR) / 100;
const parsedMinTrades = parseInt(parts[2] || '5', 10);
const minTrades = isNaN(parsedMinTrades) || parsedMinTrades <= 0 ? 5 : parsedMinTrades;
const whales = polyTracker.getProfitableWhales(minWR, minTrades);
if (whales.length === 0) {
return `No profitable whales found with WR >= ${(minWR * 100).toFixed(0)}% and >= ${minTrades} trades.\nTry lower thresholds: \`/whales profitable 50 3\``;
}
let output = `**Profitable Whales** (WR >= ${(minWR * 100).toFixed(0)}%, trades >= ${minTrades})\n\n`;
for (let i = 0; i < whales.length; i++) {
const w = whales[i];
output += ` ${i + 1}. \`${w.address.slice(0, 12)}...\`\n`;
output += ` WR: ${w.winRate.toFixed(1)}% | Avg return: $${w.avgReturn.toFixed(2)} | Value: $${w.totalValue.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n`;
}
return output;
}
case 'profile': {
if (!parts[1]) return 'Usage: /whales profile <address>';
const address = parts[1];
const profile = polyTracker.getWhaleProfile(address);
if (!profile) {
// Try on-chain lookup
const wallet = await cryptoTracker.getWallet(address, 'ethereum');
const solWallet = await cryptoTracker.getWallet(address, 'solana');
const found = wallet || solWallet;
if (!found) {
return `No profile found for \`${address}\`.\nUse \`/whales watch ${address}\` to start tracking.`;
}
let output = `**Wallet Profile** (${found.chain})\n\n`;
output += `Address: \`${found.address}\`\n`;
output += `Chain: ${found.chain}\n`;
output += `Value: $${found.totalValueUsd.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n`;
if (found.labels.length > 0) output += `Labels: ${found.labels.join(', ')}\n`;
output += `Last active: ${found.lastActive.toLocaleString()}\n`;
if (found.holdings.length > 0) {
output += `\nTop Holdings:\n`;
for (const h of found.holdings.slice(0, 10)) {
output += ` ${h.symbol}: ${h.amount.toLocaleString()} ($${h.valueUsd.toLocaleString(undefined, { maximumFractionDigits: 0 })}) - ${h.pctOfPortfolio.toFixed(1)}%\n`;
}
}
return output;
}
let output = `**Whale Profile** (Polymarket)\n\n`;
output += `Address: \`${profile.address}\`\n`;
output += `Total value: $${profile.totalValue.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n`;
output += `Win rate: ${profile.winRate.toFixed(1)}%\n`;
output += `Avg return: $${profile.avgReturn.toFixed(2)}\n`;
output += `First seen: ${profile.firstSeen.toLocaleString()}\n`;
output += `Last active: ${profile.lastActive.toLocaleString()}\n`;
if (profile.positions.length > 0) {
output += `\n**Active Positions** (${profile.positions.length})\n`;
for (const p of profile.positions.slice(0, 10)) {
output += ` ${p.outcome} $${p.usdValue.toLocaleString(undefined, { maximumFractionDigits: 0 })} @ ${p.avgEntryPrice.toFixed(2)}`;
if (p.marketQuestion) output += ` - ${p.marketQuestion.slice(0, 50)}`;
if (p.unrealizedPnl) output += ` (PnL: $${p.unrealizedPnl.toFixed(2)})`;
output += '\n';
}
}
if (profile.recentTrades.length > 0) {
output += `\n**Recent Trades** (${profile.recentTrades.length})\n`;
for (const t of profile.recentTrades.slice(0, 10)) {
const time = t.timestamp.toLocaleTimeString();
output += ` [${time}] ${t.side} ${t.outcome} $${t.usdValue.toLocaleString(undefined, { maximumFractionDigits: 0 })} @ ${t.price.toFixed(2)}\n`;
}
}
// Signal strength
const strength = polyTracker.calculateSignalStrength(profile);
output += `\nSignal strength: ${(strength * 100).toFixed(0)}%\n`;
return output;
}
case 'positions': {
const marketId = parts[1];
const positions = polyTracker.getActivePositions(marketId);
if (positions.length === 0) {
return marketId
? `No whale positions found for market \`${marketId}\`.`
: 'No active whale positions tracked.';
}
let output = `**Active Whale Positions**`;
if (marketId) output += ` (market: ${marketId.slice(0, 20)}...)`;
output += ` (${positions.length})\n\n`;
for (const p of positions.slice(0, 20)) {
output += ` \`${p.address.slice(0, 10)}...\` ${p.outcome} $${p.usdValue.toLocaleString(undefined, { maximumFractionDigits: 0 })} @ ${p.avgEntryPrice.toFixed(2)}`;
if (p.marketQuestion) output += `\n ${p.marketQuestion.slice(0, 60)}`;
if (p.unrealizedPnl) output += ` | PnL: $${p.unrealizedPnl.toFixed(2)}`;
output += '\n';
}
return output;
}
case 'watched':
case 'list': {
const watched = cryptoTracker.getWatchedWallets();
const polyWhales = polyTracker.getKnownWhales();
let output = '**Tracked Wallets**\n\n';
if (polyWhales.length > 0) {
output += `Polymarket (${polyWhales.length}):\n`;
for (const w of polyWhales.slice(0, 15)) {
output += ` \`${w.address.slice(0, 12)}...\` - $${w.totalValue.toLocaleString(undefined, { maximumFractionDigits: 0 })} | WR: ${w.winRate.toFixed(1)}%\n`;
}
if (polyWhales.length > 15) output += ` ...and ${polyWhales.length - 15} more\n`;
} else {
output += 'Polymarket: none tracked\n';
}
if (watched.size > 0) {
output += `\nOn-Chain (${watched.size}):\n`;
let count = 0;
for (const [key, w] of watched) {
if (count >= 15) {
output += ` ...and ${watched.size - 15} more\n`;
break;
}
output += ` \`${w.address.slice(0, 12)}...\` [${w.chain}] - $${w.totalValueUsd.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n`;
count++;
}
} else {
output += '\nOn-Chain: none tracked\n';
}
return output;
}
case 'start': {
const polyState = polyTracker.getConnectionState();
const cryptoStats = cryptoTracker.getStats();
let output = '**Starting Whale Tracking**\n\n';
if (polyState !== 'connected') {
try {
await polyTracker.start();
output += 'Polymarket tracker: started\n';
} catch (e) {
output += `Polymarket tracker: failed to start (${e instanceof Error ? e.message : String(e)})\n`;
}
} else {
output += 'Polymarket tracker: already running\n';
}
if (!cryptoStats.running) {
try {
await cryptoTracker.start();
output += 'Crypto tracker: started\n';
} catch (e) {
output += `Crypto tracker: failed to start (${e instanceof Error ? e.message : String(e)})\n`;
}
} else {
output += 'Crypto tracker: already running\n';
}
return output;
}
case 'stop': {
let output = '**Stopping Whale Tracking**\n\n';
try {
await polyTracker.stop();
output += 'Polymarket tracker: stopped\n';
} catch (e) {
output += `Polymarket tracker: error stopping (${e instanceof Error ? e.message : String(e)})\n`;
}
try {
await cryptoTracker.stop();
output += 'Crypto tracker: stopped\n';
} catch (e) {
output += `Crypto tracker: error stopping (${e instanceof Error ? e.message : String(e)})\n`;
}
return output;
}
case 'track': {
// Alias for 'watch'
if (!parts[1]) return 'Usage: /whale track <address> [--chain <chain>]\n\nTracks an address across Polymarket and on-chain.';
const address = parts[1];
const chainIdx = parts.indexOf('--chain');
const chain = chainIdx >= 0 ? parts[chainIdx + 1] : undefined;
const validChains = ['solana', 'ethereum', 'polygon', 'arbitrum', 'base', 'optimism'];
polyTracker.trackAddress(address);
if (chain && validChains.includes(chain)) {
cryptoTracker.watchWallet(address, chain as any);
} else {
cryptoTracker.watchWallet(address);
}
let output = `**Now Tracking** \`${address}\`\n\n`;
output += `Polymarket: tracking\n`;
output += `On-chain: ${chain || 'all configured chains'}\n`;
return output;
}
case 'untrack': {
// Alias for 'unwatch'
if (!parts[1]) return 'Usage: /whale untrack <address>';
const address = parts[1];
polyTracker.untrackAddress(address);
cryptoTracker.unwatchWallet(address);
return `Stopped tracking \`${address}\` on all platforms.`;
}
case 'activity': {
const query = parts.slice(1).join(' ');
if (!query) return 'Usage: /whale activity <market-id-or-keyword>';
// Try to get whale activity for the market
try {
const activity = await polyMod.getMarketWhaleActivity(query);
let output = `**Whale Activity for Market**\n\n`;
output += `Total whale volume: $${activity.totalWhaleVolume.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n`;
output += `Buy volume: $${activity.buyVolume.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n`;
output += `Sell volume: $${activity.sellVolume.toLocaleString(undefined, { maximumFractionDigits: 0 })}\n\n`;
if (activity.topBuyers.length > 0) {
output += `Top buyers:\n`;
for (const addr of activity.topBuyers.slice(0, 5)) {
output += ` \`${addr}\`\n`;
}
}
if (activity.topSellers.length > 0) {
output += `\nTop sellers:\n`;
for (const addr of activity.topSellers.slice(0, 5)) {
output += ` \`${addr}\`\n`;
}
}
return output;
} catch (e) {
return `Could not get whale activity for "${query}": ${e instanceof Error ? e.message : String(e)}`;
}
}
case 'recent': {
const parsedLimit = parseInt(parts[1] || '20', 10);
const limit = isNaN(parsedLimit) || parsedLimit <= 0 ? 20 : parsedLimit;
const minSizeIdx = parts.indexOf('--min-size');
const parsedMinSize = minSizeIdx >= 0 ? parseInt(parts[minSizeIdx + 1], 10) : 0;
const minSize = isNaN(parsedMinSize) ? 0 : parsedMinSize;
let polyTrades = polyTracker.getRecentTrades(limit);
if (minSize > 0) {
polyTrades = polyTrades.filter(t => t.usdValue >= minSize);
}
if (polyTrades.length === 0) {
return `No recent whale trades${minSize > 0 ? ` above $${minSize.toLocaleString()}` : ''}. Tracker may need to be started.`;
}
let output = `**Recent Whale Trades** (${polyTrades.length})`;
if (minSize > 0) output += ` (min $${minSize.toLocaleString()})`;
output += '\n\n';
for (const t of polyTrades) {
const time = t.timestamp.toLocaleTimeString();
output += ` [${time}] ${t.side} ${t.outcome} $${t.usdValue.toLocaleString(undefined, { maximumFractionDigits: 0 })} @ ${t.price.toFixed(2)}`;
if (t.marketQuestion) output += `\n ${t.marketQuestion.slice(0, 70)}`;
output += '\n';
}
return output;
}
case 'config': {
const stats = cryptoTracker.getStats();
const polyState = polyTracker.getConnectionState();
return `**Whale Tracking Config**
Polymarket:
WebSocket: ${polyState}
Min trade size: $10,000
Min position size: $50,000
On-Chain:
Running: ${stats.running ? 'Yes' : 'No'}
Chains: ${stats.chains.join(', ') || 'none configured'}
Min tx value: $50,000
Min whale value: $1,000,000
Watched wallets: ${stats.watchedWallets}
Poll interval: 30s
Use \`/whales watch <address>\` to add wallets.
Use \`/whales polymarket market <id>\` to check specific markets.`;
}
default:
return helpText();
}
} catch (error) {
return `Error: ${error instanceof Error ? error.message : String(error)}`;
}
}
function helpText(): string {
return `**Whale Tracking Commands**
/whale - Active whale alerts (all platforms)
/whale start - Start whale monitoring
/whale stop - Stop whale monitoring
/whale track <addr> [--chain c] - Follow specific wallet
/whale untrack <addr> - Stop following wallet
/whale recent [n] [--min-size N] - Recent whale trades
/whale activity <market> - Whale activity for market
/whale polymarket [n] - Polymarket whale trades
/whale polymarket market <id> - Whale activity for a market
/whale crypto [chain] [n] - On-chain whale txs
/whale watch <addr> [--chain c] - Track an address
/whale unwatch <addr> - Stop tracking
/whale list - List tracked wallets
/whale top [n] - Top Polymarket whales
/whale top crypto [chain] [n] - Top on-chain whales
/whale profitable [wr%] [min-n] - Profitable whales
/whale profile <addr> - Whale profile + positions
/whale positions [market-id] - Active whale positions
/whale config - Tracking config`;
}
export default {
name: 'whale-tracking',
description: 'Whale tracking across Polymarket and on-chain (Solana, EVM)',
commands: ['/whales', '/whale', '/whale-tracking'],
handle: execute,
};