Files
alsk1992andClaude Opus 4.5 bd48a192ec Clean repo for production release
- Remove apps/ (docs website → separate repo)
- Remove trading/ (Python reference scripts)
- Remove ui/, vendor/, workspace/ (dev placeholders)
- Remove assets/, demo.gif, demo.tape (use hosted images)
- Remove internal planning docs (PLAN.md, TODO.md, etc.)
- Update .gitignore to exclude dev files
- Update README to use cloddsbot.com hosted images
- Update all URLs from clodds.vercel.app → cloddsbot.com
- Fix Polymarket fee claims (0% on most markets)
- Fix order types (postOnly boolean, not POST_ONLY)
- Add startup validation for ANTHROPIC_API_KEY
- Add fee-adjusted arbitrage calculations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 20:27:58 +00:00

331 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Demo GIF capture script
* Uses Puppeteer to create an animated demo GIF
*/
const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const ASSETS_DIR = path.join(__dirname, '..', 'assets');
const FRAMES_DIR = path.join(ASSETS_DIR, 'frames');
// Ensure directories exist
if (!fs.existsSync(FRAMES_DIR)) {
fs.mkdirSync(FRAMES_DIR, { recursive: true });
}
// Clean old frames
fs.readdirSync(FRAMES_DIR).forEach(f => fs.unlinkSync(path.join(FRAMES_DIR, f)));
const conversation = [
{ type: 'user', text: 'search bitcoin polymarket' },
{ type: 'bot', text: `<span style="color:#22d3ee">🔍 Found 4 markets:</span>
<pre>
<span style="color:#fbbf24">1. BTC $150k by June 2026</span>
YES: <span style="color:#4ade80">38¢</span> NO: <span style="color:#f87171">62¢</span> Vol: $4.2M
<span style="color:#fbbf24">2. BTC $200k by Dec 2026</span>
YES: <span style="color:#4ade80">22¢</span> NO: <span style="color:#f87171">78¢</span> Vol: $1.8M
<span style="color:#fbbf24">3. BTC flips gold market cap</span>
YES: <span style="color:#4ade80">8¢</span> NO: <span style="color:#f87171">92¢</span> Vol: $620K
</pre>` },
{ type: 'user', text: 'find arbitrage' },
{ type: 'bot', text: `<span style="color:#22d3ee">⚡ Scanning 9 platforms...</span>
<pre>
<span style="color:#4ade80">Found 3 arbitrage opportunities:</span>
<span style="color:#22d3ee">1. Fed Rate Cut March</span>
Polymarket: <span style="color:#4ade80">42¢</span> | Kalshi: <span style="color:#f87171">55¢</span>
<span style="color:#fbbf24">→ 3.0% arb • Kelly: 6.1%</span>
<span style="color:#22d3ee">2. BTC $150k June</span>
Betfair: <span style="color:#4ade80">35¢</span> | Polymarket: <span style="color:#f87171">61¢</span>
<span style="color:#fbbf24">→ 4.0% arb • Kelly: 8.2%</span>
</pre>` },
{ type: 'user', text: 'buy 500 YES btc 150k polymarket' },
{ type: 'bot', text: `<span style="color:#22d3ee">🔄 Executing on Polymarket...</span>
<pre>
<span style="color:#4ade80">✅ Order filled!</span>
500 YES @ $0.38 • Cost: $190
Max payout: $500 • EV: +$120
<span style="color:#94a3b8">💾 Trade logged to database</span>
</pre>` },
{ type: 'user', text: 'long ETH 25x $500 hyperliquid' },
{ type: 'bot', text: `<span style="color:#22d3ee">📈 Opening leveraged position...</span>
<pre>
<span style="color:#4ade80">✅ Position opened on Hyperliquid!</span>
Long ETH-PERP @ $3,420
Size: $500 × 25x = $12,500 notional
Liq: $3,280 | TP: $3,600 | SL: $3,350
<span style="color:#94a3b8">💾 Logged • Strategy: momentum_breakout</span>
</pre>` },
{ type: 'user', text: 'swap 200 USDC to SOL jupiter' },
{ type: 'bot', text: `<span style="color:#22d3ee">🔄 Routing via Jupiter...</span>
<pre>
<span style="color:#4ade80">✅ Swap complete!</span>
200 USDC → 1.04 SOL @ $192.30
Slippage: 0.05% • Route: USDC→SOL
<span style="color:#94a3b8">🛡️ MEV protected via Jito bundle</span>
</pre>` },
{ type: 'user', text: 'show whales' },
{ type: 'bot', text: `<span style="color:#22d3ee">🐋 Top Whales (24h)</span>
<pre>
<span style="color:#fbbf24">1. 0x7c22...d795</span> <span style="color:#4ade80">+$52,400</span>
72% win rate • 156 trades
Latest: 8000 YES "ETH ETF" @ 0.62
<span style="color:#fbbf24">2. Theo</span> <span style="color:#4ade80">+$31,200</span>
68% win rate • 89 trades
</pre>` },
{ type: 'user', text: 'copy 0x7c22 10%' },
{ type: 'bot', text: `<span style="color:#4ade80">✅ Copy trading enabled!</span>
<pre>
Tracking: 0x7c22...d795
Size: 10% of positions • Max: $100/trade
<span style="color:#94a3b8">Auto-mirroring trades with 2s delay</span>
</pre>` },
{ type: 'user', text: 'alert me when btc 150k hits 50c' },
{ type: 'bot', text: `<span style="color:#4ade80">🔔 Alert created!</span>
<pre>
Market: BTC $150k by June
Trigger: YES price ≥ $0.50
Channel: Telegram + WebChat
<span style="color:#94a3b8">You'll be notified instantly</span>
</pre>` },
{ type: 'user', text: 'backtest momentum strategy btc' },
{ type: 'bot', text: `<span style="color:#22d3ee">📊 Backtesting momentum_breakout...</span>
<pre>
Period: 90 days • 847 simulated trades
<span style="color:#4ade80">Results:</span>
Win rate: 62.4% • Profit factor: 1.84
Sharpe: 2.1 • Max drawdown: -8.2%
Total return: <span style="color:#4ade80">+34.6%</span>
<span style="color:#94a3b8">💾 Results saved to strategy_backtests</span>
</pre>` },
{ type: 'user', text: 'compare with mean reversion' },
{ type: 'bot', text: `<span style="color:#22d3ee">🔬 A/B Strategy Comparison</span>
<pre>
│ Momentum │ Mean Rev
──────────────┼──────────┼──────────
Win Rate │ 62.4% │ 58.1%
Sharpe Ratio │ 2.1 │ 1.7
Max Drawdown │ -8.2% │ -12.4%
Total Return │ <span style="color:#4ade80">+34.6%</span> │ <span style="color:#4ade80">+28.2%</span>
<span style="color:#fbbf24">→ Momentum outperforms by +6.4%</span>
<span style="color:#94a3b8">💾 Comparison saved to ab_tests table</span>
</pre>` },
{ type: 'user', text: 'portfolio' },
{ type: 'bot', text: `<span style="color:#22d3ee">📊 Portfolio Summary</span>
<pre>
┌────────────────────────────────────────┐
│ Position │ Side │ Entry │ P&L │
├────────────────────────────────────────┤
│ <span style="color:#22d3ee">BTC $150k Jun</span> │500 YES│ $0.38 │<span style="color:#4ade80">+$35</span>│
│ <span style="color:#22d3ee">ETH-PERP 25x</span> │ Long │$3,420 │<span style="color:#4ade80">+$68</span>│
│ <span style="color:#22d3ee">SOL</span> │ 1.04 │$192.30│<span style="color:#4ade80">+$12</span>│
├────────────────────────────────────────┤
│ <span style="color:#fbbf24">Total: +$115 (+8.4%)</span> Sharpe: 2.3 │
│ <span style="color:#94a3b8">Alerts: 1 • Copying: 1 wallet</span> │
│ <span style="color:#94a3b8">💾 All trades logged to SQLite</span> │
└────────────────────────────────────────┘
</pre>` },
];
function generateHTML(messages) {
// Match landing page dark theme with cyan accents
return `
<!DOCTYPE html>
<html>
<head>
<title>Clodds WebChat</title>
<style>
* { box-sizing: border-box; }
body {
font-family: system-ui, -apple-system, sans-serif;
max-width: 650px;
margin: 0 auto;
padding: 16px;
background: linear-gradient(180deg, #0f172a 0%, #1e293b 50%, #0f172a 100%);
min-height: 100%;
color: #e2e8f0;
}
.header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
padding: 14px;
background: rgba(0,0,0,0.3);
border-radius: 12px;
border: 1px solid #334155;
}
.header img { width: 40px; height: 40px; border-radius: 10px; }
.header h1 {
margin: 0;
font-size: 20px;
background: linear-gradient(180deg, #fff 0%, #22d3ee 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
#messages {
height: 500px;
overflow-y: auto;
border: 1px solid #334155;
padding: 12px;
margin-bottom: 12px;
background: rgba(30, 41, 59, 0.5);
border-radius: 12px;
display: flex;
flex-direction: column;
}
.msg { margin: 10px 0; padding: 12px 16px; border-radius: 12px; line-height: 1.5; }
.user {
background: linear-gradient(135deg, #0891b2 0%, #06b6d4 100%);
color: #0f172a;
text-align: right;
margin-left: 20%;
font-weight: 500;
}
.bot {
background: #334155;
color: #e2e8f0;
margin-right: 10%;
border: 1px solid #475569;
}
.bot pre {
margin: 8px 0 0 0;
font-family: 'SF Mono', Monaco, monospace;
font-size: 11px;
white-space: pre-wrap;
line-height: 1.5;
color: #cbd5e1;
}
.system {
background: rgba(34, 211, 238, 0.1);
border: 1px solid rgba(34, 211, 238, 0.3);
color: #22d3ee;
font-size: 0.85em;
text-align: center;
}
#input-area { display: flex; gap: 10px; }
#input {
flex: 1;
padding: 14px 18px;
border: 1px solid #334155;
border-radius: 12px;
font-size: 14px;
background: #1e293b;
color: #e2e8f0;
}
#input::placeholder { color: #64748b; }
button {
padding: 14px 28px;
background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%);
color: #0f172a;
border: none;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
}
</style>
</head>
<body>
<div class="header">
<img src="https://cloddsbot.com/logo.png" alt="Clodds" />
<h1>Clodds WebChat</h1>
</div>
<div id="messages">
<div class="msg system">Connected. Ready!</div>
${messages.map(m => `<div class="msg ${m.type}">${m.text}</div>`).join('\n')}
</div>
<div id="input-area">
<input type="text" id="input" placeholder="Ask about prediction markets..." />
<button>Send</button>
</div>
<script>
document.getElementById('messages').scrollTop = document.getElementById('messages').scrollHeight;
</script>
</body>
</html>`;
}
async function captureDemo() {
console.log('Launching browser...');
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
try {
const page = await browser.newPage();
await page.setViewport({ width: 700, height: 700 });
let frameNum = 0;
const captureFrame = async (duration = 1) => {
for (let i = 0; i < duration; i++) {
const framePath = path.join(FRAMES_DIR, `frame_${String(frameNum++).padStart(4, '0')}.png`);
await page.screenshot({ path: framePath, type: 'png' });
}
};
// Start with empty chat
console.log('Capturing frames...');
await page.setContent(generateHTML([]));
await captureFrame(10); // Pause at start
// Add messages one by one
const visibleMessages = [];
for (const msg of conversation) {
visibleMessages.push(msg);
await page.setContent(generateHTML(visibleMessages));
if (msg.type === 'user') {
await captureFrame(8); // Short pause for user message
} else {
await captureFrame(25); // Longer pause to read bot response
}
}
// Final pause
await captureFrame(20);
console.log(`Captured ${frameNum} frames`);
// Generate GIF with ffmpeg
console.log('Generating GIF...');
const gifPath = path.join(ASSETS_DIR, 'demo.gif');
try {
execSync(`ffmpeg -y -framerate 10 -i "${FRAMES_DIR}/frame_%04d.png" -vf "fps=10,scale=700:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=256[p];[s1][p]paletteuse=dither=floyd_steinberg" -loop 0 "${gifPath}"`, {
stdio: 'inherit'
});
console.log('✅ Demo GIF created:', gifPath);
// Copy to docs public
const docsGif = path.join(__dirname, '..', 'apps', 'docs', 'public', 'demo.gif');
fs.copyFileSync(gifPath, docsGif);
console.log('✅ Copied to:', docsGif);
// Show file size
const stats = fs.statSync(gifPath);
console.log(` Size: ${(stats.size / 1024).toFixed(1)} KB`);
} catch (e) {
console.error('ffmpeg failed:', e.message);
console.log('Try: brew install ffmpeg');
}
} finally {
await browser.close();
}
}
captureDemo().catch(console.error);