Build an AI Trading Agent with LunarCrush + Google Gemini in 20 Minutes
Transform social media buzz into actionable trading signals using real-time sentiment analysis and AI-powered recommendations
Why Social Beats Traditional Analysis
Most traders rely on price charts and technical indicators - but by then, you're already behind. Social media buzz happens before price movements, giving you a crucial edge. The challenge? Processing thousands of social mentions manually is impossible.
That's where AI-powered social sentiment analysis comes in.
What You'll Build
In this tutorial, you'll create a production-ready AI Trading Agent that:
- ✅ Analyzes 5 cryptocurrencies using LunarCrush's unique social metrics
- ✅ Generates BUY/SELL/HOLD signals with Google Gemini AI and confidence scores
- ✅ Tracks progress in real-time through a 7-step analysis pipeline
- ✅ Stores results in PostgreSQL with live dashboard updates
Time Investment: 20 minutes
Skill Level: Beginner to Intermediate
What You'll Learn: Next.js, TypeScript, AI integration, real-time data processing, background jobs
💡 Pro Tip: By the end, you'll have a portfolio-worthy project that demonstrates modern AI development patterns!
Live Example: View the deployed version →
Before We Start
You'll Need:
- Node.js 18+ installed
- Basic knowledge of React/TypeScript
- A code editor (VS Code recommended)
- 5 API keys from different services (we'll walk through signup below)
Two Ways to Experience This Tutorial:
- 👨💻 Build It Yourself - Follow along step-by-step with your own API keys
- 🚀 Try the Live Demo - View the deployed version and clone the repo to explore
Quick Project Setup:
# We'll build this step-by-step, but here's the final structure:
npx create-next-app@latest ai-trading-agent --typescript --tailwind --app
cd ai-trading-agent
npm install @supabase/supabase-js inngest @google/generative-ai lucide-react
🚨 Common Issue: Make sure you have Node.js 18+ installed. Check with node --version
Account Setup Guide
We need 5 services for this project. Don't worry - most have generous free tiers!
Sign Up For LunarCrush API
LunarCrush provides social sentiment data that most traders don't have access to.
- Visit LunarCrush Signup
- Enter your email address and click "Continue"
- Check your email for verification code and enter it
- Complete the onboarding steps:
- Select your favorite categories (or keep defaults)
- Create your profile (add photo and nickname if desired)
- Important: Select a subscription plan (you'll need it to generate an API key)
Generate Your API Key
Once you've subscribed, navigate to the API authentication page and generate an API key.
Save this API key - you'll add it to your environment variables later.
Set Up Google Gemini AI
Google's Gemini AI will analyze the social data and generate trading recommendations.
- Sign up: Visit aistudio.google.com and click "Get API key"
- Choose authentication: Sign in with your Google account
-
Create API key:
- Click "Create API key"
- Choose "Create API key in new project" or select existing project
- Copy your API key (starts with
AIza...
)
Set Up Supabase Database
Supabase provides our PostgreSQL database with real-time subscriptions.
- Sign up: Visit supabase.com and click "Start your project"
- Create organization: Choose a name for your organization
-
Create project:
- Project name: "ai-trading-agent"
- Database password: Create a secure password
- Region: Choose closest to you
- Pricing plan: Free tier is perfect for this tutorial
-
Get your project credentials:
- Go to Project Overview
- Copy your Project URL (looks like
https://xxx.supabase.co
) - Copy your anon public API Key (starts with
eyJ...
)
Set Up Inngest Background Jobs
Inngest handles our multi-step AI analysis pipeline.
- Create account: Visit inngest.com and click "Sign up"
-
Create new app:
- App name: "ai-trading-agent"
-
Get your keys: In the Inngest dashboard, go to Settings → Keys
- Copy your Event Key (starts with
inngest_
) - Copy your Signing Key (starts with
signkey_
)
- Copy your Event Key (starts with
Project Setup
Now let's build our AI trading agent step by step.
Create Next.js Project
# Create new Next.js project with TypeScript and Tailwind
npx create-next-app@latest ai-trading-agent --typescript --tailwind --app
cd ai-trading-agent
# Install required dependencies
npm install @supabase/supabase-js inngest @google/generative-ai lucide-react clsx tailwind-merge uuid
npm install @types/uuid --save-dev
# Create environment file
touch .env.local
Set Up Environment Variables
Add your API keys to .env.local
:
# .env.local
LUNARCRUSH_API_KEY=lc_your_key_here
GOOGLE_GEMINI_API_KEY=your_gemini_key_here
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key_here
INNGEST_EVENT_KEY=inngest_your_key_here
INNGEST_SIGNING_KEY=signkey_your_signing_key_here
Create Project Structure (Copy/Paste Terminal Commands)
# Create directory structure
mkdir -p src/lib src/functions src/types src/app/api/{signals,trigger,inngest}
# Create TypeScript interfaces
cat > src/types/trading.ts << 'EOF'
export interface SocialMetrics {
symbol: string;
mentions: number; // Social posts mentioning the asset
interactions: number; // Total social engagements
creators: number; // Unique content creators
altRank: number; // LunarCrush proprietary ranking
galaxyScore: number; // Health indicator (0-100)
timestamp: number;
}
export interface TradingSignal {
id: string;
symbol: string;
signal: 'BUY' | 'SELL' | 'HOLD';
confidence: number; // 0-100 confidence score
reasoning: string; // AI explanation
metrics: SocialMetrics; // Social data that generated this signal
createdAt: string;
}
EOF
Database Setup
Create Database Schema
In your Supabase dashboard:
- Go to SQL Editor
- Click "New query"
- Copy and paste this script:
-- Trading signals table
CREATE TABLE trading_signals (
id TEXT PRIMARY KEY,
symbol TEXT NOT NULL,
signal TEXT NOT NULL CHECK (signal IN ('BUY', 'SELL', 'HOLD')),
confidence INTEGER NOT NULL CHECK (confidence >= 0 AND confidence <= 100),
reasoning TEXT NOT NULL,
metrics JSONB NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Analysis jobs table for real-time progress tracking
CREATE TABLE analysis_jobs (
id TEXT PRIMARY KEY,
status TEXT NOT NULL DEFAULT 'started',
current_step TEXT DEFAULT 'Initializing...',
step_message TEXT DEFAULT 'Starting analysis...',
progress_percentage INTEGER DEFAULT 0,
event_data JSONB,
signals_generated INTEGER DEFAULT 0,
duration_ms INTEGER,
started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
completed_at TIMESTAMP WITH TIME ZONE,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Performance indexes
CREATE INDEX idx_trading_signals_symbol ON trading_signals (symbol);
CREATE INDEX idx_trading_signals_created_at ON trading_signals (created_at DESC);
CREATE INDEX idx_analysis_jobs_status ON analysis_jobs (status);
- Click "Run" to create the tables
Core Implementation (Copy/Paste Terminal Commands)
LunarCrush API Client
# Create LunarCrush API client
cat > src/lib/lunarcrush.ts << 'EOF'
import type { SocialMetrics } from '@/types/trading';
const BASE_URL = 'https://lunarcrush.com/api4/public';
const getApiKey = () => {
const apiKey = process.env.LUNARCRUSH_API_KEY;
if (!apiKey) {
throw new Error('LUNARCRUSH_API_KEY environment variable is required');
}
return apiKey;
};
const makeRequest = async <T>(endpoint: string): Promise<T> => {
const url = `${BASE_URL}${endpoint}`;
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${getApiKey()}`,
'Content-Type': 'application/json',
},
signal: AbortSignal.timeout(10000),
});
if (!response.ok) {
if (response.status === 401) {
throw new Error('Invalid API key - check your LunarCrush credentials');
}
if (response.status === 429) {
throw new Error('Rate limit exceeded - upgrade your plan or try again later');
}
throw new Error(`LunarCrush API Error: ${response.status}`);
}
return await response.json();
};
export async function getSocialMetrics(symbol: string): Promise<SocialMetrics> {
try {
// Fetch ranking data (altRank, galaxyScore)
const coinsResponse = await makeRequest<any>('/coins/list/v1?limit=500&sort=alt_rank');
const coinData = coinsResponse.data.find(
(coin: any) => coin.symbol.toUpperCase() === symbol.toUpperCase()
);
// Fetch social data (mentions, interactions, creators)
const topicResponse = await makeRequest<any>(`/topic/${symbol}/v1`);
const topicData = topicResponse.data;
return {
symbol: symbol.toUpperCase(),
mentions: topicData.num_posts || 0,
interactions: topicData.interactions_24h || 0,
creators: topicData.num_contributors || 0,
altRank: coinData?.alt_rank || 999,
galaxyScore: coinData?.galaxy_score || 0,
timestamp: Date.now(),
};
} catch (error) {
console.error(`Failed to fetch social metrics for ${symbol}:`, error);
throw error;
}
}
EOF
Google Gemini AI Integration
# Create Gemini AI client
cat > src/lib/gemini.ts << 'EOF'
import { GoogleGenerativeAI } from '@google/generative-ai';
import type { SocialMetrics, TradingSignal } from '@/types/trading';
const getGeminiClient = () => {
const apiKey = process.env.GOOGLE_GEMINI_API_KEY;
if (!apiKey) {
throw new Error('GOOGLE_GEMINI_API_KEY environment variable is required');
}
return new GoogleGenerativeAI(apiKey);
};
export async function generateTradingSignal(
symbol: string,
metrics: SocialMetrics
): Promise<TradingSignal> {
try {
const genAI = getGeminiClient();
const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' });
const prompt = `You are a crypto trading analyst. Analyze these LunarCrush social metrics for ${symbol} and provide a trading signal.
Current Metrics for ${symbol}:
- Social Mentions: ${metrics.mentions.toLocaleString()} posts in 24h
- Total Interactions: ${metrics.interactions.toLocaleString()} engagements
- Unique Creators: ${metrics.creators.toLocaleString()} content creators
- AltRank: ${metrics.altRank} (lower = better, proprietary ranking)
- Galaxy Score: ${metrics.galaxyScore}/100 (LunarCrush health indicator)
ANALYSIS FRAMEWORK:
1. Social Volume Surge: High mentions + interactions = increased attention
2. Creator Diversity: More unique creators = broader interest, less manipulation
3. AltRank Position: Lower rank = stronger market + social performance
4. Galaxy Score Health: Higher score = better ecosystem health
5. Engagement Quality: Interactions per mention ratio
Generate a trading signal with this EXACT format:
SIGNAL: [BUY/SELL/HOLD]
CONFIDENCE: [0-100]
REASONING: [2-3 sentences explaining the decision focusing on the metrics above]
Keep reasoning concise and focus on what makes LunarCrush data unique.`;
const result = await model.generateContent(prompt);
const response = await result.response;
const analysis = response.text();
// Parse AI response
const signalMatch = analysis.match(/SIGNAL:\s*(BUY|SELL|HOLD)/i);
const confidenceMatch = analysis.match(/CONFIDENCE:\s*(\d+)/i);
const reasoningMatch = analysis.match(/REASONING:\s*(.+?)(?=\n\n|\n$|$)/s);
const signal = (signalMatch?.[1]?.toUpperCase() as 'BUY' | 'SELL' | 'HOLD') || 'HOLD';
const confidence = parseInt(confidenceMatch?.[1] || '50');
const reasoning = reasoningMatch?.[1]?.trim() || 'Analysis based on social metrics';
return {
id: `${symbol}-${Date.now()}`,
symbol,
signal,
confidence: Math.max(0, Math.min(100, confidence)),
reasoning,
metrics,
createdAt: new Date().toISOString(),
};
} catch (error) {
console.error('Gemini AI Error:', error);
throw error;
}
}
EOF
Database & Background Job Setup
# Create Supabase client
cat > src/lib/supabase.ts << 'EOF'
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
if (!supabaseUrl || !supabaseAnonKey) {
throw new Error('Missing Supabase environment variables');
}
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
EOF
# Create Inngest client
cat > src/lib/inngest.ts << 'EOF'
import { Inngest } from 'inngest';
export const inngest = new Inngest({
id: 'ai-trading-agent',
name: 'AI Trading Agent',
});
EOF
Background Workflow Function
# Create the main AI analysis workflow
cat > src/functions/signal-analysis.ts << 'EOF'
import { inngest } from '@/lib/inngest';
import { getSocialMetrics } from '@/lib/lunarcrush';
import { generateTradingSignal } from '@/lib/gemini';
import { supabase } from '@/lib/supabase';
import type { TradingSignal } from '@/types/trading';
export const signalAnalysisWorkflow = inngest.createFunction(
{ id: 'signal-analysis-workflow' },
{ event: 'trading.analyze' },
async ({ event, step }) => {
const startTime = Date.now();
const jobId = event.data.jobId;
if (!jobId) {
throw new Error('No job ID provided in event data');
}
const updateProgress = async (
stepNumber: number,
stepName: string,
stepMessage: string,
status: string = 'started'
) => {
const progressPercentage = Math.round((stepNumber / 7) * 100);
await supabase
.from('analysis_jobs')
.update({
current_step: stepName,
step_message: stepMessage,
progress_percentage: progressPercentage,
status: status,
updated_at: new Date().toISOString(),
})
.eq('id', jobId);
};
// Step 1: Initialize job
await step.run('initialize-job', async () => {
await supabase.from('analysis_jobs').insert({
id: jobId,
status: 'started',
current_step: 'Initializing Analysis',
step_message: 'Setting up trading analysis pipeline...',
progress_percentage: 14,
event_data: event.data,
started_at: new Date().toISOString(),
});
return jobId;
});
// Step 2: Get symbols
const symbols = await step.run('get-symbols', async () => {
await updateProgress(2, 'Preparing Symbol List', 'Selecting cryptocurrencies for analysis...');
return event.data.symbols || ['BTC', 'ETH', 'SOL', 'ADA', 'DOT'];
});
// Step 3: Fetch social metrics
const socialMetrics = await step.run('fetch-social-metrics', async () => {
await updateProgress(3, 'Fetching Social Data', 'Gathering real-time sentiment from LunarCrush...');
const results = [];
for (let i = 0; i < symbols.length; i++) {
const symbol = symbols[i];
try {
await updateProgress(3, 'Fetching Social Data', `Analyzing ${symbol} (${i + 1}/${symbols.length})...`);
const metrics = await getSocialMetrics(symbol);
results.push({ symbol, metrics, success: true });
await new Promise((resolve) => setTimeout(resolve, 1500)); // Rate limiting
} catch (error) {
console.error(`Failed to fetch ${symbol}:`, error);
results.push({ symbol, metrics: null, success: false });
}
}
return results;
});
// Step 4: Generate AI signals
const tradingSignals = await step.run('generate-ai-signals', async () => {
await updateProgress(4, 'AI Signal Generation', 'Google Gemini analyzing social patterns...');
const signals: TradingSignal[] = [];
const successfulMetrics = socialMetrics.filter((r) => r.success && r.metrics);
for (let i = 0; i < successfulMetrics.length; i++) {
const result = successfulMetrics[i];
try {
await updateProgress(4, 'AI Signal Generation', `AI analyzing ${result.symbol} (${i + 1}/${successfulMetrics.length})...`);
const signal = await generateTradingSignal(result.symbol, result.metrics!);
signals.push(signal);
await new Promise((resolve) => setTimeout(resolve, 3000)); // Rate limiting for Gemini
} catch (error) {
console.error(`AI analysis failed for ${result.symbol}:`, error);
}
}
return signals;
});
// Step 5: Save to database
await step.run('save-to-database', async () => {
await updateProgress(5, 'Saving Results', `Storing ${tradingSignals.length} trading signals...`);
for (const signal of tradingSignals) {
await supabase.from('trading_signals').insert({
id: signal.id,
symbol: signal.symbol,
signal: signal.signal,
confidence: signal.confidence,
reasoning: signal.reasoning,
metrics: signal.metrics,
created_at: signal.createdAt,
});
}
});
// Step 6: Generate summary
const summary = await step.run('generate-summary', async () => {
await updateProgress(6, 'Generating Summary', 'Creating analysis summary...');
return {
totalAnalyzed: tradingSignals.length,
highConfidence: tradingSignals.filter((s) => s.confidence >= 70).length,
distribution: {
BUY: tradingSignals.filter((s) => s.signal === 'BUY').length,
SELL: tradingSignals.filter((s) => s.signal === 'SELL').length,
HOLD: tradingSignals.filter((s) => s.signal === 'HOLD').length,
},
};
});
// Step 7: Complete
await step.run('complete-job', async () => {
const duration = Date.now() - startTime;
await updateProgress(
7,
'Analysis Complete',
`Generated ${tradingSignals.length} trading signals in ${Math.round(duration / 1000)}s`,
'completed'
);
await supabase
.from('analysis_jobs')
.update({
signals_generated: tradingSignals.length,
duration_ms: duration,
completed_at: new Date().toISOString(),
})
.eq('id', jobId);
});
return { success: true, jobId, duration: Date.now() - startTime, summary };
}
);
EOF
API Routes & Dashboard
Create API Endpoints
# Create signals API route
cat > src/app/api/signals/route.ts << 'EOF'
import { NextResponse } from 'next/server';
import { supabase } from '@/lib/supabase';
export async function GET() {
try {
const { data: signals, error: signalsError } = await supabase
.from('trading_signals')
.select('*')
.order('created_at', { ascending: false })
.limit(20);
if (signalsError) throw signalsError;
const { data: jobs, error: jobsError } = await supabase
.from('analysis_jobs')
.select('*')
.order('started_at', { ascending: false })
.limit(10);
return NextResponse.json({
success: true,
signals: signals || [],
jobs: jobs || [],
timestamp: new Date().toISOString(),
});
} catch (error) {
console.error('Error fetching signals:', error);
return NextResponse.json(
{ success: false, error: 'Failed to fetch trading signals' },
{ status: 500 }
);
}
}
EOF
# Create trigger API route
cat > src/app/api/trigger/route.ts << 'EOF'
import { inngest } from '@/lib/inngest';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { symbols } = body;
const jobId = `job_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const eventData = {
jobId,
symbols: symbols || ['BTC', 'ETH', 'SOL', 'ADA', 'DOT'],
timestamp: Date.now(),
triggerType: 'manual',
};
const eventId = await inngest.send({
name: 'trading.analyze',
data: eventData,
});
return NextResponse.json({
success: true,
jobId,
eventId,
message: 'Analysis job queued successfully',
});
} catch (error) {
console.error('Failed to trigger analysis:', error);
return NextResponse.json(
{ success: false, error: 'Failed to queue processing job' },
{ status: 500 }
);
}
}
EOF
# Create Inngest webhook route
cat > src/app/api/inngest/route.ts << 'EOF'
import { serve } from 'inngest/next';
import { inngest } from '@/lib/inngest';
import { signalAnalysisWorkflow } from '@/functions/signal-analysis';
export const { GET, POST, PUT } = serve({
client: inngest,
functions: [signalAnalysisWorkflow],
});
EOF
Create the Dashboard
# Create the main dashboard (simplified version)
cat > src/app/page.tsx << 'EOF'
'use client';
import { useState, useEffect } from 'react';
import { supabase } from '@/lib/supabase';
import { Activity, TrendingUp, Users, MessageSquare, Zap } from 'lucide-react';
interface TradingSignal {
id: string;
symbol: string;
signal: 'BUY' | 'SELL' | 'HOLD';
confidence: number;
reasoning: string;
metrics: any;
created_at: string;
}
export default function Dashboard() {
const [signals, setSignals] = useState<TradingSignal[]>([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
fetchSignals();
}, []);
const fetchSignals = async () => {
try {
const response = await fetch('/api/signals');
const data = await response.json();
setSignals(data.signals || []);
} catch (error) {
console.error('Failed to fetch signals:', error);
}
};
const triggerAnalysis = async () => {
setIsLoading(true);
try {
const response = await fetch('/api/trigger', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ symbols: ['BTC', 'ETH', 'SOL', 'ADA', 'DOT'] }),
});
if (response.ok) {
// Refresh signals after a delay
setTimeout(() => {
fetchSignals();
setIsLoading(false);
}, 45000); // Wait 45 seconds for analysis to complete
}
} catch (error) {
console.error('Failed to trigger analysis:', error);
setIsLoading(false);
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900">
{/* Header */}
<div className="border-b border-gray-700/50 bg-gray-900/80 backdrop-blur-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex items-center space-x-4">
<div className="w-10 h-10 bg-gradient-to-r from-blue-600 to-purple-600 rounded-lg flex items-center justify-center">
<Activity className="h-6 w-6 text-white" />
</div>
<div>
<h1 className="text-xl font-bold text-white">AI Trading Agent</h1>
<p className="text-sm text-gray-400">Powered by LunarCrush & Google Gemini</p>
</div>
</div>
<div className="text-right">
<div className="text-sm text-gray-300">{signals.length} Active Signals</div>
</div>
</div>
</div>
</div>
{/* Main Content */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Trigger Button */}
<div className="mb-8">
<button
onClick={triggerAnalysis}
disabled={isLoading}
className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 disabled:from-gray-600 disabled:to-gray-700 text-white font-semibold py-3 px-6 rounded-lg transition-all duration-200 flex items-center space-x-2"
>
{isLoading ? (
<>
<div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
<span>Analyzing...</span>
</>
) : (
<>
<Zap className="h-4 w-4" />
<span>Generate Trading Signals</span>
</>
)}
</button>
</div>
{/* Signals Grid */}
{signals.length > 0 ? (
<div className="grid grid-cols-1 xl:grid-cols-2 gap-6">
{signals.slice(0, 10).map((signal) => (
<SignalCard key={signal.id} signal={signal} />
))}
</div>
) : (
<div className="text-center py-12">
<p className="text-gray-400">No signals yet. Click the button above to generate your first trading signals!</p>
</div>
)}
</div>
</div>
);
}
// Signal Card Component
function SignalCard({ signal }: { signal: TradingSignal }) {
const getSignalColor = (signalType: string) => {
switch (signalType) {
case 'BUY': return 'bg-green-500/10 border-green-500/20 text-green-400';
case 'SELL': return 'bg-red-500/10 border-red-500/20 text-red-400';
case 'HOLD': return 'bg-yellow-500/10 border-yellow-500/20 text-yellow-400';
default: return 'bg-gray-500/10 border-gray-500/20 text-gray-400';
}
};
const formatNumber = (num: number) => {
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;
if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;
return num.toString();
};
return (
<div className="bg-gray-900/50 border border-gray-700/50 rounded-xl p-6 hover:border-gray-600/50 transition-all duration-300">
{/* Header */}
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-blue-500/20 rounded-lg flex items-center justify-center">
<span className="text-blue-400 font-bold text-sm">{signal.symbol}</span>
</div>
<div>
<h3 className="text-white font-semibold text-lg">${signal.symbol}</h3>
<p className="text-gray-400 text-sm">
{new Date(signal.created_at).toLocaleString()}
</p>
</div>
</div>
<div className={`px-3 py-1.5 rounded-full border ${getSignalColor(signal.signal)}`}>
<span className="font-semibold text-sm">{signal.signal}</span>
</div>
</div>
{/* Confidence Score */}
<div className="mb-4">
<div className="flex items-center justify-between mb-2">
<span className="text-gray-300 text-sm">AI Confidence</span>
<span className="text-white font-semibold">{signal.confidence}%</span>
</div>
<div className="w-full bg-gray-700/50 rounded-full h-2">
<div
className={`h-2 rounded-full transition-all duration-500 ${
signal.confidence >= 80 ? 'bg-green-500' :
signal.confidence >= 60 ? 'bg-yellow-500' : 'bg-red-500'
}`}
style={{ width: `${signal.confidence}%` }}
/>
</div>
</div>
{/* LunarCrush Metrics */}
<div className="grid grid-cols-2 gap-4 mb-4">
<div className="bg-gray-800/50 rounded-lg p-3">
<div className="flex items-center space-x-2 mb-1">
<MessageSquare className="h-4 w-4 text-blue-400" />
<span className="text-gray-400 text-xs">Mentions</span>
</div>
<span className="text-white font-semibold">
{formatNumber(signal.metrics.mentions)}
</span>
</div>
<div className="bg-gray-800/50 rounded-lg p-3">
<div className="flex items-center space-x-2 mb-1">
<Users className="h-4 w-4 text-green-400" />
<span className="text-gray-400 text-xs">Creators</span>
</div>
<span className="text-white font-semibold">{signal.metrics.creators}</span>
</div>
</div>
{/* AI Reasoning */}
<div className="bg-gray-800/30 rounded-lg p-4">
<h4 className="text-gray-300 text-sm font-medium mb-2">AI Analysis</h4>
<p className="text-gray-300 text-sm leading-relaxed">{signal.reasoning}</p>
</div>
</div>
);
}
EOF
Testing & Deployment
Local Testing
# Start both servers
npm run dev # Terminal 1: Next.js (localhost:3000)
npx inngest-cli dev # Terminal 2: Inngest (localhost:8288)
Verify Everything Works:
- 🔍 Dashboard loads: Visit localhost:3000
- 🚀 Analysis triggers: Click "Generate Trading Signals"
- 📊 Inngest processes: Check localhost:8288 for job execution
- 💾 Database saves: Verify records in your Supabase dashboard
Debugging Tip: Use localhost:8288
to monitor Inngest execution. If jobs fail, check API keys and rate limits.
Deploy to Production
Deploy on Vercel (Recommended):
Manual Deployment Steps:
- Push to GitHub:
git init
git add .
git commit -m "AI Trading Agent with LunarCrush social analytics"
git branch -M main
git remote add origin https://github.com/yourusername/ai-trading-agent-gemini.git
git push -u origin main
-
Connect to Vercel:
- Visit vercel.com and sign up
- Import your GitHub repository
- Configure project settings
-
Add Environment Variables in Vercel:
- Go to Settings → Environment Variables
- Add all variables from your
.env.local
:
LUNARCRUSH_API_KEY=lc_your_api_key_here
GOOGLE_GEMINI_API_KEY=your_gemini_key_here
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key_here
INNGEST_EVENT_KEY=inngest_your_event_key_here
INNGEST_SIGNING_KEY=signkey_your_signing_key_here
-
Configure Inngest for Production:
- In Inngest dashboard, create a production app
- Add your deployment URL:
https://your-app.vercel.app/api/inngest
- Update environment variables with production keys
-
Deploy:
- Click "Deploy" in Vercel
- Wait for deployment to complete
- Test your live application
Live Example: You can see a deployed version of this trading agent at https://ai-trading-agent-gemini.vercel.app/
Level Up: Adding Advanced Features
Want to make this even more impressive? Here are some powerful extensions:
AI Enhancement Prompts
Here are AI prompts you can use to extend this project:
Component Generation:
"Create a React component that displays cryptocurrency sentiment trends over time using the LunarCrush social metrics data structure: { mentions, interactions, creators, altRank, galaxyScore }. Include line charts showing 7-day trends and highlight significant changes."
Error Handling:
"Add comprehensive error handling to this cryptocurrency sentiment analysis function, including network timeouts, API rate limits, invalid responses, and graceful degradation when social data is unavailable."
Mobile Optimization:
"Convert this desktop trading signals dashboard to be mobile-responsive using Tailwind CSS, with swipeable signal cards, collapsible metrics sections, and optimized touch interactions for mobile traders."
Advanced Analytics:
"Create a signal accuracy tracking system that monitors the performance of AI trading recommendations over time, calculating success rates, profit/loss potential, and confidence score reliability using historical price data."
Troubleshooting Common Issues
Here are solutions to the most common problems you might encounter:
Issue | Solution |
---|---|
Inngest Function Not Triggering | Check your INNGEST_EVENT_KEY and ensure the Inngest dev server is running. Verify the /api/inngest route is accessible |
Database Connection Errors | Verify Supabase credentials in environment variables. Ensure tables were created successfully using the provided SQL schema |
401 Unauthorized (LunarCrush) | Your API key is invalid or subscription is inactive. Check the key format in your .env.local file and verify your LunarCrush subscription |
Environment Variables Missing | In production, ensure all environment variables are set in your deployment platform (Vercel/Netlify) |
Rate Limiting (429 Errors) | Individual plan is limited to 10 requests/minute. Add caching or reduce processing frequency for large datasets |
🔍 Pro Debugging Tip: Check the Inngest dashboard at localhost:8288
during development to see detailed execution logs for each step of your workflow.
Ready to Scale?
LunarCrush Plan Benefits:
- Real-time data for faster signal generation
- Higher rate limits for production traffic
- Advanced metrics and additional social indicators
- Priority support and custom integrations
Conclusion
Congratulations! You've successfully built a production-ready AI Trading Agent that demonstrates cutting-edge development practices. This system showcases:
What You've Accomplished
- ✅ Real-time Social Analytics - LunarCrush integration with unique metrics
- ✅ AI-Powered Decision Making - Google Gemini generating intelligent trading signals
- ✅ Background Job Processing - Inngest orchestrating complex workflows
- ✅ Real-time Data Pipeline - Supabase subscriptions for live updates
- ✅ Production Architecture - Error handling, rate limiting, and monitoring
- ✅ Professional UI/UX - Beautiful dashboard with progress tracking
What's Next?
Extend Your Trading Agent:
- Add historical signal tracking and performance analytics
- Implement portfolio management and position sizing
- Create custom alert systems with email/SMS notifications
- Upgrade to LunarCrush Builder plan for real-time data and higher limits
Production Optimizations:
- Set up monitoring with Vercel Analytics or Sentry
- Add rate limiting and request validation
- Implement caching for frequently accessed data
- Configure auto-scaling for high traffic
Advanced Features:
- Multi-exchange Integration - Connect to Binance, Coinbase APIs
- Risk Management - Add stop-loss and take-profit automation
- Backtesting Engine - Test strategies against historical data
- Mobile App - React Native version for iOS/Android
🚀 Take Action
Get Started Now:
- Subscribe To LunarCrush API - Get access to unique social metrics
- Fork the Repository - Start building your own version
- Join the Community - Share your enhancements
Share Your Success:
- Tweet your deployed app with #LunarCrush #AITrading
- Write about your experience on LinkedIn
- Submit to developer showcases and communities
Resources
- LunarCrush API Documentation
- LunarCrush Pricing Plans
- Google Gemini AI Documentation
- Inngest Documentation
- Supabase Documentation
🚀 GitHub Repository (Complete Code)
Built with ❤️ using LunarCrush • Google Gemini • Inngest • Supabase • Next.js
Questions? Drop them below! I respond to every comment and love helping fellow developers build amazing applications with social sentiment analysis. 🚀
Ready to turn social buzz into trading profits? Start building your AI trading agent today!
Top comments (1)
Sick!
Some comments may only be visible to logged-in visitors. Sign in to view all comments.