Build an Insider Trading Discord Bot in Node.js
Academic research shows insider cluster buys — multiple executives purchasing shares within days of each other — predict 12-month abnormal returns of 7-13% (Lakonishok & Lee, 2001). This tutorial builds a Discord bot that detects them automatically using free SEC Form 4 data.
~15 min read · Intermediate Node.js
What Are Insider Cluster Buys?
When a single insider buys stock, it's a data point. When 3+ insiders buy within a 2-week window, it's a signal. These "cluster buys" suggest insiders collectively believe the stock is undervalued.
The Insider Trading API provides normalized Form 4 data: transaction type (buy/sell/option exercise), dollar amounts, insider role (CEO/CFO/Director), and filing dates — everything you need to detect clusters.
Step 1: Set Up the Project
mkdir insider-bot && cd insider-bot npm init -y npm install discord.js node-fetch@2 # Create .env (never commit this!) echo "DISCORD_TOKEN=your_bot_token_here" > .env echo "CHANNEL_ID=your_channel_id_here" >> .env
Step 2: Build the Cluster Detector
// cluster.js — Detects insider cluster buys
const fetch = require('node-fetch');
const CLUSTER_WINDOW_DAYS = 14;
const MIN_INSIDERS = 3;
async function detectClusters(ticker) {
const url = `https://securitiesdb.com/api/v1/stocks/${ticker}/insider-trading`;
const res = await fetch(url);
if (!res.ok) return null;
const { data } = await res.json();
const buys = data.transactions
.filter(t => t.type === 'purchase' && t.value > 10000)
.sort((a, b) => new Date(b.date) - new Date(a.date));
// Sliding window: find groups of 3+ buys within 14 days
for (let i = 0; i < buys.length - MIN_INSIDERS + 1; i++) {
const window = [buys[i]];
for (let j = i + 1; j < buys.length; j++) {
const daysDiff = (new Date(buys[i].date) - new Date(buys[j].date))
/ (1000 * 60 * 60 * 24);
if (daysDiff <= CLUSTER_WINDOW_DAYS) window.push(buys[j]);
}
if (window.length >= MIN_INSIDERS) {
const uniqueInsiders = new Set(window.map(t => t.insider_name));
if (uniqueInsiders.size >= MIN_INSIDERS) {
return {
ticker,
cluster_size: uniqueInsiders.size,
total_value: window.reduce((s, t) => s + t.value, 0),
window_start: window[window.length - 1].date,
window_end: window[0].date,
insiders: [...uniqueInsiders],
};
}
}
}
return null;
}
module.exports = { detectClusters };Step 3: Wire Up the Discord Bot
// bot.js — Discord bot that scans for insider clusters
require('dotenv').config();
const { Client, GatewayIntentBits, EmbedBuilder } = require('discord.js');
const { detectClusters } = require('./cluster');
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
});
// Watchlist — expand as needed
const WATCHLIST = [
"AAPL", "MSFT", "GOOGL", "AMZN", "META", "TSLA", "NVDA",
"JPM", "BAC", "GS", "V", "MA", "UNH", "JNJ", "PFE",
];
async function scanAndAlert() {
const channel = await client.channels.fetch(process.env.CHANNEL_ID);
for (const ticker of WATCHLIST) {
const cluster = await detectClusters(ticker);
if (!cluster) continue;
const embed = new EmbedBuilder()
.setColor(0x00ff00)
.setTitle(`🔔 Insider Cluster Buy: ${cluster.ticker}`)
.setDescription(
`**${cluster.cluster_size} insiders** bought within ${
Math.ceil((new Date(cluster.window_end) - new Date(cluster.window_start))
/ (1000*60*60*24))
} days`
)
.addFields(
{ name: 'Total Value', value: `$${(cluster.total_value).toLocaleString()}`, inline: true },
{ name: 'Window', value: `${cluster.window_start} → ${cluster.window_end}`, inline: true },
{ name: 'Insiders', value: cluster.insiders.join(', ') },
)
.setFooter({ text: 'Data: SecuritiesDB Free API · SEC Form 4' });
await channel.send({ embeds: [embed] });
// Respect rate limits
await new Promise(r => setTimeout(r, 1000));
}
}
client.on('ready', () => {
console.log(`Bot online as ${client.user.tag}`);
scanAndAlert(); // Run immediately
setInterval(scanAndAlert, 6 * 60 * 60 * 1000); // Then every 6 hours
});
client.login(process.env.DISCORD_TOKEN);Step 4: Combine with Quant Scores
A cluster buy is more meaningful when the company is financially healthy. Cross-reference with quant scores:
// Enhance alerts with quant health context
async function enrichCluster(cluster) {
const url = `https://securitiesdb.com/api/v1/stocks/${cluster.ticker}/quant-health`;
const res = await fetch(url);
if (!res.ok) return cluster;
const { data } = await res.json();
return {
...cluster,
piotroski: data.scores.piotroski_f,
altman_z: data.scores.altman_z,
signal_strength: data.scores.piotroski_f >= 7 ? '🟢 Strong' : '🟡 Moderate',
};
}What You Built
- An automated insider cluster buy scanner using free SEC data
- Discord notifications when 3+ insiders buy within 14 days
- Cross-referencing with Piotroski and Altman Z scores for signal strength
- Runs 24/7 with a simple Node.js process
Total code: ~100 lines. Zero API cost. Services like Fintel charge $400/year for similar insider alerting.