agent-loop / static /index.html
arudradey's picture
Deploy Solana Hedge Fund with fast local small AI
981a2a2 verified
Raw History Blame Contribute Delete
32.3 kB
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SOLANA // QUANT • Autonomous Hedge Fund</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'],
mono: ['JetBrains Mono', 'ui-monospace', 'monospace']
},
colors: {
sol: {
purple: '#9945FF',
green: '#14F195',
},
zinc: {
850: '#1c1c22',
900: '#111115',
925: '#0c0c0f',
950: '#060608',
}
}
}
}
}
</script>
<!-- Google Fonts: Inter & JetBrains Mono -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Lucide Icons -->
<script src="https://unpkg.com/lucide@latest"></script>
<style>
body {
background-color: #060608;
color: #fafafa;
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
-webkit-font-smoothing: antialiased;
}
::selection {
background: #14F195;
color: #000000;
}
.custom-scroll::-webkit-scrollbar {
width: 5px;
height: 5px;
}
.custom-scroll::-webkit-scrollbar-track {
background: transparent;
}
.custom-scroll::-webkit-scrollbar-thumb {
background: #27272a;
border-radius: 9999px;
}
.custom-scroll::-webkit-scrollbar-thumb:hover {
background: #3f3f46;
}
</style>
</head>
<body class="min-h-screen flex flex-col font-sans selection:bg-sol-green selection:text-black">
<!-- Top Minimalist Bar -->
<header class="border-b border-zinc-800/80 bg-zinc-950/90 backdrop-blur-md sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between">
<!-- Brand & Status -->
<div class="flex items-center space-x-4">
<a href="/" class="flex items-center space-x-2 group">
<div class="w-7 h-7 rounded-lg bg-gradient-to-tr from-sol-purple to-sol-green p-[1px] flex items-center justify-center shadow-lg shadow-sol-green/10">
<div class="w-full h-full bg-zinc-950 rounded-[7px] flex items-center justify-center font-mono font-black text-xs text-sol-green">
◎
</div>
</div>
<div>
<div class="font-extrabold text-sm sm:text-base tracking-tight uppercase text-white">
SOLANA <span class="text-zinc-500 font-normal">//</span> QUANT
</div>
<div class="text-[10px] font-mono text-zinc-500 -mt-0.5">Autonomous Hedge Fund</div>
</div>
</a>
<div class="hidden sm:flex items-center space-x-2 pl-3 border-l border-zinc-800 text-xs font-mono">
<span class="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-sol-green/10 text-sol-green text-[11px] font-semibold">
<span class="w-1.5 h-1.5 rounded-full bg-sol-green animate-pulse"></span>
ACTIVE
</span>
<span class="text-zinc-500 text-[11px]">Qwen2.5-0.5B (Local CPU)</span>
</div>
</div>
<!-- Navigation Tabs (Segmented Control) -->
<nav class="flex items-center p-1 rounded-lg bg-zinc-900 border border-zinc-800 text-xs font-medium">
<button onclick="switchTab('overview')" id="btn-tab-overview" class="px-3 py-1.5 rounded-md bg-white text-zinc-950 font-bold transition">
Overview
</button>
<button onclick="switchTab('ai')" id="btn-tab-ai" class="px-3 py-1.5 rounded-md text-zinc-400 hover:text-white transition">
AI Copilot
</button>
<button onclick="switchTab('arbitrage')" id="btn-tab-arbitrage" class="px-3 py-1.5 rounded-md text-zinc-400 hover:text-white transition">
DEX Spreads
</button>
<button onclick="switchTab('blotter')" id="btn-tab-blotter" class="px-3 py-1.5 rounded-md text-zinc-400 hover:text-white transition">
Blotter
</button>
<button onclick="switchTab('storage')" id="btn-tab-storage" class="px-3 py-1.5 rounded-md text-zinc-400 hover:text-white transition">
Storage
</button>
</nav>
<!-- External Links -->
<div class="hidden md:flex items-center space-x-3 text-xs font-mono">
<a href="/docs" target="_blank" class="text-zinc-400 hover:text-white transition">
/docs
</a>
<a href="https://huggingface.co/spaces/arudradey/agent-loop" target="_blank" class="px-3 py-1.5 rounded-lg border border-zinc-700 bg-zinc-900 hover:bg-zinc-800 text-zinc-200 font-semibold transition flex items-center space-x-1">
<span>HF Space</span>
<i data-lucide="arrow-up-right" class="w-3.5 h-3.5"></i>
</a>
</div>
</div>
</header>
<!-- Live Ticker Strip -->
<div class="border-b border-zinc-800/60 bg-zinc-950/60 px-4 py-2 text-xs font-mono overflow-x-auto whitespace-nowrap custom-scroll">
<div class="max-w-7xl mx-auto flex items-center justify-between gap-6 text-[11px]">
<div class="flex items-center space-x-6">
<span class="flex items-center space-x-1.5">
<span class="text-zinc-500 font-semibold">SOL/USD:</span>
<span id="ticker-sol" class="text-white font-bold">$148.50</span>
<span class="text-sol-green font-semibold">+4.8%</span>
</span>
<span class="flex items-center space-x-1.5">
<span class="text-zinc-500 font-semibold">JUP/USD:</span>
<span id="ticker-jup" class="text-white font-bold">$0.985</span>
<span class="text-sol-green font-semibold">+2.1%</span>
</span>
<span class="flex items-center space-x-1.5">
<span class="text-zinc-500 font-semibold">RAY/USD:</span>
<span id="ticker-ray" class="text-white font-bold">$2.15</span>
<span class="text-sol-green font-semibold">+6.4%</span>
</span>
<span class="flex items-center space-x-1.5">
<span class="text-zinc-500 font-semibold">WIF/USD:</span>
<span id="ticker-wif" class="text-white font-bold">$1.84</span>
<span class="text-red-400 font-semibold">-1.2%</span>
</span>
</div>
<div class="text-zinc-500 hidden sm:block">
Jupiter Aggregator v6 &bull; Raydium CPMM &bull; Orca Whirlpools
</div>
</div>
</div>
<!-- Main Container -->
<main class="flex-1 max-w-7xl w-full mx-auto p-4 sm:p-6 lg:p-8">
<!-- ============================================================= -->
<!-- TAB 1: OVERVIEW & POSITIONS -->
<!-- ============================================================= -->
<div id="tab-overview" class="space-y-6">
<!-- Bold AUM & Performance Metrics -->
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4">
<div class="p-5 rounded-2xl border border-zinc-800 bg-zinc-925/80">
<div class="text-[11px] font-mono uppercase tracking-wider text-zinc-500">Total Fund AUM</div>
<div id="stat-aum" class="text-2xl sm:text-3xl font-black font-mono tracking-tight text-white mt-1">$14,250,000</div>
<div class="text-xs font-mono text-sol-green mt-1">+$482,150 (+3.49% 24h)</div>
</div>
<div class="p-5 rounded-2xl border border-zinc-800 bg-zinc-925/80">
<div class="text-[11px] font-mono uppercase tracking-wider text-zinc-500">Solana Exposure</div>
<div id="stat-sol-exposure" class="text-2xl sm:text-3xl font-black font-mono tracking-tight text-white mt-1">56.5%</div>
<div class="text-xs font-mono text-zinc-400 mt-1">54,200 SOL ($8.04M)</div>
</div>
<div class="p-5 rounded-2xl border border-zinc-800 bg-zinc-925/80">
<div class="text-[11px] font-mono uppercase tracking-wider text-zinc-500">Sharpe Ratio</div>
<div class="text-2xl sm:text-3xl font-black font-mono tracking-tight text-white mt-1">3.42</div>
<div class="text-xs font-mono text-zinc-400 mt-1">Max Drawdown: 4.1%</div>
</div>
<div class="p-5 rounded-2xl border border-zinc-800 bg-zinc-925/80">
<div class="text-[11px] font-mono uppercase tracking-wider text-zinc-500">AI Quant Engine</div>
<div class="text-2xl sm:text-3xl font-black font-mono tracking-tight text-sol-green mt-1">0.5B CPU</div>
<div class="text-xs font-mono text-zinc-400 mt-1">Sub-second inference</div>
</div>
</div>
<!-- Two Column Layout: Positions & Quick Trade -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6 items-start">
<!-- Left: Asset Allocation Table (8 Cols) -->
<div class="lg:col-span-8 rounded-2xl border border-zinc-800 bg-zinc-925/50 p-6 space-y-4 shadow-xl">
<div class="flex items-center justify-between border-b border-zinc-800 pb-3">
<h2 class="text-xs font-mono font-bold tracking-wider uppercase text-zinc-200 flex items-center space-x-2">
<i data-lucide="pie-chart" class="w-4 h-4 text-sol-purple"></i>
<span>Active Portfolio Positions</span>
</h2>
<span class="text-xs font-mono text-zinc-500">Auto-Rebalance: <strong class="text-sol-green">ON</strong></span>
</div>
<div class="rounded-xl border border-zinc-800/80 overflow-hidden font-mono text-xs">
<div class="grid grid-cols-12 bg-zinc-950 p-3 text-zinc-500 font-semibold border-b border-zinc-800">
<span class="col-span-4">ASSET</span>
<span class="col-span-3 text-right">HOLDINGS</span>
<span class="col-span-3 text-right">VALUE (USDC)</span>
<span class="col-span-2 text-right">WEIGHT</span>
</div>
<div id="positions-table" class="divide-y divide-zinc-900 bg-zinc-925/40">
<!-- Rendered via JS -->
</div>
</div>
</div>
<!-- Right: Instant Order Dispatch (4 Cols) -->
<div class="lg:col-span-4 rounded-2xl border border-zinc-800 bg-zinc-925/50 p-6 space-y-4 shadow-xl">
<div class="flex items-center justify-between border-b border-zinc-800 pb-3">
<h2 class="text-xs font-mono font-bold tracking-wider uppercase text-zinc-200 flex items-center space-x-2">
<i data-lucide="zap" class="w-4 h-4 text-sol-green"></i>
<span>Instant DEX Order</span>
</h2>
<span class="text-[10px] font-mono px-2 py-0.5 rounded bg-zinc-900 border border-zinc-800 text-zinc-400">JUPITER v6</span>
</div>
<div class="space-y-3 font-mono text-xs">
<div>
<label class="block text-zinc-500 mb-1">Asset</label>
<select id="trade-symbol" class="w-full bg-zinc-950 border border-zinc-800 rounded-lg p-2 text-white font-bold focus:outline-none">
<option value="SOL">SOL (Solana)</option>
<option value="JUP">JUP (Jupiter)</option>
<option value="RAY">RAY (Raydium)</option>
<option value="WIF">WIF (dogwifhat)</option>
</select>
</div>
<div>
<label class="block text-zinc-500 mb-1">Side</label>
<div class="grid grid-cols-2 gap-2">
<button onclick="setTradeSide('BUY')" id="btn-side-buy" class="py-1.5 rounded-lg border font-bold text-xs bg-sol-green/10 text-sol-green border-sol-green/30">BUY</button>
<button onclick="setTradeSide('SELL')" id="btn-side-sell" class="py-1.5 rounded-lg border border-zinc-800 bg-zinc-900 text-zinc-400 hover:text-white font-bold text-xs">SELL</button>
</div>
</div>
<div>
<label class="block text-zinc-500 mb-1">Notional Size (USDC)</label>
<input id="trade-amount" type="number" value="100000" class="w-full bg-zinc-950 border border-zinc-800 rounded-lg p-2 text-white font-bold focus:outline-none">
</div>
<div class="pt-2">
<button onclick="submitTradeOrder()" id="btn-submit-order" class="w-full py-2.5 rounded-lg bg-white hover:bg-zinc-200 text-black font-extrabold text-xs uppercase tracking-wider transition flex items-center justify-center space-x-2">
<span>Dispatch To Solana</span>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- ============================================================= -->
<!-- TAB 2: LOCAL SMALL AI QUANT COPILOT -->
<!-- ============================================================= -->
<div id="tab-ai" class="space-y-6 hidden">
<div class="rounded-2xl border border-zinc-800 bg-zinc-925/80 p-6 sm:p-8 space-y-6 shadow-2xl">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div>
<div class="flex items-center space-x-2">
<span class="px-2 py-0.5 rounded text-[10px] font-mono font-bold bg-white text-black uppercase">Qwen2.5-0.5B-Instruct</span>
<span class="px-2 py-0.5 rounded text-[10px] font-mono font-bold bg-sol-green/10 text-sol-green border border-sol-green/20 uppercase">494M Params &bull; Local CPU</span>
</div>
<h1 class="text-2xl sm:text-3xl font-black tracking-tight text-white uppercase mt-2">
SolQuant Autonomous Copilot
</h1>
<p class="text-xs sm:text-sm text-zinc-400 mt-1">
High-frequency on-chain trading signals, Raydium/Orca arbitrage analysis, and portfolio risk management generated directly on CPU in milliseconds.
</p>
</div>
<div class="text-right font-mono text-xs text-zinc-500">
<div>Engine: <span class="text-white font-bold">PyTorch SIMD</span></div>
<div>Latency: <span class="text-sol-green font-bold">&lt; 15ms TTFT</span></div>
</div>
</div>
<!-- Presets -->
<div class="flex flex-wrap gap-2 text-xs font-mono">
<span class="text-zinc-500 self-center">Presets:</span>
<button onclick="setAiPrompt('sol_momentum')" class="px-2.5 py-1 rounded-md bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 text-zinc-300 transition">
$SOL Momentum & Sizing
</button>
<button onclick="setAiPrompt('arb_scan')" class="px-2.5 py-1 rounded-md bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 text-zinc-300 transition">
Raydium vs Orca Arbitrage
</button>
<button onclick="setAiPrompt('rebalance')" class="px-2.5 py-1 rounded-md bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 text-zinc-300 transition">
Portfolio Rebalancing
</button>
</div>
<!-- Prompt Textarea -->
<div class="relative">
<textarea id="ai-prompt" rows="3" class="w-full rounded-xl bg-zinc-950 border border-zinc-800 focus:border-zinc-500 focus:ring-1 focus:ring-zinc-500 p-4 text-sm font-medium text-white placeholder-zinc-600 focus:outline-none transition resize-none custom-scroll leading-relaxed" placeholder="Ask SolQuant AI for portfolio signals, arbitrage strategies, or risk sizing...">Analyze current SOL/USDC momentum, calculate 24h risk-adjusted sizing, and recommend optimal slippage bounds for Raydium liquidity pools.</textarea>
</div>
<!-- Trigger Button -->
<div class="flex items-center justify-between pt-1 font-mono text-xs">
<span class="text-zinc-500">Zero external API dependencies &bull; runs 100% locally</span>
<button onclick="queryQuantAi()" id="btn-run-ai" class="px-6 py-2.5 rounded-lg bg-white hover:bg-zinc-200 text-black font-extrabold text-xs uppercase tracking-wider transition flex items-center space-x-2 shadow-lg active:scale-95">
<i data-lucide="zap" class="w-4 h-4 fill-black"></i>
<span>Run Analysis</span>
</button>
</div>
</div>
<!-- Real-Time AI Stream Output -->
<div class="rounded-2xl border border-zinc-800 bg-zinc-925/50 p-6 space-y-4 shadow-xl">
<div class="flex items-center justify-between border-b border-zinc-800 pb-3">
<div class="flex items-center space-x-2 text-xs font-mono font-bold text-zinc-200 uppercase">
<i data-lucide="cpu" class="w-4 h-4 text-sol-green"></i>
<span>Live Quant Intelligence Output</span>
</div>
<button onclick="copyAiOutput()" id="btn-copy-ai" class="text-[11px] font-mono px-2 py-1 rounded bg-zinc-900 border border-zinc-800 text-zinc-400 hover:text-white transition">
COPY
</button>
</div>
<pre id="ai-output-box" class="p-4 rounded-xl bg-zinc-950 border border-zinc-900 text-zinc-200 font-mono text-xs whitespace-pre-wrap leading-relaxed min-h-[220px] max-h-[480px] overflow-y-auto custom-scroll">
Enter an objective above and click "Run Analysis" to receive real-time quant alpha.
</pre>
</div>
</div>
<!-- ============================================================= -->
<!-- TAB 3: DEX ARBITRAGE SPREADS -->
<!-- ============================================================= -->
<div id="tab-arbitrage" class="space-y-6 hidden">
<div class="rounded-2xl border border-zinc-800 bg-zinc-925/50 p-6 space-y-4 shadow-xl">
<div class="flex items-center justify-between border-b border-zinc-800 pb-3">
<h2 class="text-xs font-mono font-bold tracking-wider uppercase text-zinc-200 flex items-center space-x-2">
<i data-lucide="refresh-ccw" class="w-4 h-4 text-sol-green"></i>
<span>Cross-DEX Arbitrage Opportunities</span>
</h2>
<span class="text-xs font-mono text-zinc-500">Scan Interval: <strong class="text-white">100ms</strong></span>
</div>
<div id="arb-cards-container" class="grid grid-cols-1 md:grid-cols-3 gap-4">
<!-- Rendered via JS -->
</div>
</div>
</div>
<!-- ============================================================= -->
<!-- TAB 4: TRADE BLOTTER (Persistent Bucket Log) -->
<!-- ============================================================= -->
<div id="tab-blotter" class="space-y-6 hidden">
<div class="rounded-2xl border border-zinc-800 bg-zinc-925/50 p-6 space-y-4 shadow-xl">
<div class="flex items-center justify-between border-b border-zinc-800 pb-3">
<h2 class="text-xs font-mono font-bold tracking-wider uppercase text-zinc-200 flex items-center space-x-2">
<i data-lucide="list" class="w-4 h-4 text-sol-purple"></i>
<span>On-Chain Trade Execution Blotter</span>
</h2>
<span class="text-xs font-mono text-zinc-500">Saved to <strong class="text-zinc-300">/data/trades/trades.json</strong></span>
</div>
<div class="rounded-xl border border-zinc-800/80 overflow-hidden font-mono text-xs">
<div class="grid grid-cols-12 bg-zinc-950 p-3 text-zinc-500 font-semibold border-b border-zinc-800">
<span class="col-span-3">TX ID</span>
<span class="col-span-2">PAIR</span>
<span class="col-span-2">SIDE</span>
<span class="col-span-3 text-right">NOTIONAL (USDC)</span>
<span class="col-span-2 text-right">STATUS</span>
</div>
<div id="blotter-table" class="divide-y divide-zinc-900 bg-zinc-925/40">
<!-- Rendered via JS -->
</div>
</div>
</div>
</div>
<!-- ============================================================= -->
<!-- TAB 5: STORAGE BUCKET EXPLORER (/data) -->
<!-- ============================================================= -->
<div id="tab-storage" class="space-y-6 hidden">
<div class="rounded-2xl border border-zinc-800 bg-zinc-925/50 p-6 space-y-4 shadow-xl">
<div class="flex items-center justify-between border-b border-zinc-800 pb-3">
<h2 class="text-xs font-mono font-bold tracking-wider uppercase text-zinc-200 flex items-center space-x-2">
<i data-lucide="hard-drive" class="w-4 h-4 text-sol-green"></i>
<span>Persistent Bucket Storage Status</span>
</h2>
<span class="text-xs font-mono text-zinc-400">Volume: <strong class="text-white">/data</strong></span>
</div>
<div id="storage-status-box" class="p-4 rounded-xl bg-zinc-950 border border-zinc-900 text-zinc-300 font-mono text-xs space-y-2">
<div>Bucket <strong class="text-white">arudradey/agent-loop-storage</strong> is active and clean.</div>
<div class="text-zinc-500">All trade logs and hedge fund parameters persist across restarts.</div>
</div>
</div>
</div>
</main>
<!-- Clean Minimalist Footer -->
<footer class="border-t border-zinc-800/80 bg-zinc-950 text-zinc-500 text-xs py-5">
<div class="max-w-7xl mx-auto px-4 sm:px-6 flex flex-col sm:flex-row items-center justify-between gap-3 font-mono">
<div>
SOLANA QUANT HEDGE FUND &bull; Qwen2.5-0.5B CPU Engine &bull; arudradey/agent-loop
</div>
<div class="flex items-center space-x-4">
<span>Volume: <strong class="text-zinc-300">/data</strong></span>
<a href="/docs" class="text-sol-green hover:underline">API Docs</a>
</div>
</div>
</footer>
<!-- Application Logic Scripts -->
<script>
let activeTradeSide = 'BUY';
function initIcons() {
lucide.createIcons();
}
function switchTab(tabId) {
const tabs = ['overview', 'ai', 'arbitrage', 'blotter', 'storage'];
tabs.forEach(t => {
const pane = document.getElementById('tab-' + t);
const btn = document.getElementById('btn-tab-' + t);
if (t === tabId) {
pane.classList.remove('hidden');
btn.className = 'px-3 py-1.5 rounded-md bg-white text-zinc-950 font-bold transition';
} else {
pane.classList.add('hidden');
btn.className = 'px-3 py-1.5 rounded-md text-zinc-400 hover:text-white transition';
}
});
if (tabId === 'overview') fetchMarket();
if (tabId === 'arbitrage') fetchMarket();
if (tabId === 'blotter') fetchTrades();
initIcons();
}
function setTradeSide(side) {
activeTradeSide = side;
const buyBtn = document.getElementById('btn-side-buy');
const sellBtn = document.getElementById('btn-side-sell');
if (side === 'BUY') {
buyBtn.className = 'py-1.5 rounded-lg border font-bold text-xs bg-sol-green/10 text-sol-green border-sol-green/30';
sellBtn.className = 'py-1.5 rounded-lg border border-zinc-800 bg-zinc-900 text-zinc-400 hover:text-white font-bold text-xs';
} else {
sellBtn.className = 'py-1.5 rounded-lg border font-bold text-xs bg-red-500/10 text-red-400 border-red-500/30';
buyBtn.className = 'py-1.5 rounded-lg border border-zinc-800 bg-zinc-900 text-zinc-400 hover:text-white font-bold text-xs';
}
}
function setAiPrompt(type) {
const p = document.getElementById('ai-prompt');
if (type === 'sol_momentum') {
p.value = "Analyze current SOL/USDC momentum, calculate 24h risk-adjusted sizing, and recommend optimal slippage bounds for Raydium liquidity pools.";
} else if (type === 'arb_scan') {
p.value = "Scan Raydium CPMM vs Orca Whirlpools for flash loan cross-DEX arbitrage spreads and calculate net yield.";
} else if (type === 'rebalance') {
p.value = "Simulate portfolio rebalancing: maintain 60% SOL, 15% JUP, 10% RAY, and 15% USDC cash buffers to minimize drawdown.";
}
}
async function fetchMarket() {
try {
const res = await fetch('/api/market');
const data = await res.json();
// Ticker
if (data.prices) {
document.getElementById('ticker-sol').textContent = '$' + data.prices.SOL;
document.getElementById('ticker-jup').textContent = '$' + data.prices.JUP;
document.getElementById('ticker-ray').textContent = '$' + data.prices.RAY;
document.getElementById('ticker-wif').textContent = '$' + data.prices.WIF;
}
// Positions
if (data.portfolio && data.portfolio.positions) {
const container = document.getElementById('positions-table');
container.innerHTML = '';
data.portfolio.positions.forEach(pos => {
const row = document.createElement('div');
row.className = 'grid grid-cols-12 p-3 items-center hover:bg-zinc-900/60 transition';
row.innerHTML = `
<div class="col-span-4 flex items-center space-x-2 font-bold text-white">
<span class="w-2 h-2 rounded-full ${pos.symbol === 'SOL' ? 'bg-sol-green' : 'bg-sol-purple'}"></span>
<span>${pos.symbol}</span>
<span class="text-zinc-500 font-normal">(${pos.name})</span>
</div>
<div class="col-span-3 text-right text-zinc-300 font-semibold">${pos.amount.toLocaleString()}</div>
<div class="col-span-3 text-right text-white font-bold">$${pos.value_usdc.toLocaleString()}</div>
<div class="col-span-2 text-right text-sol-green font-bold">${pos.allocation}%</div>
`;
container.appendChild(row);
});
}
// Arbitrage Cards
if (data.arbitrage_opportunities) {
const arbCont = document.getElementById('arb-cards-container');
arbCont.innerHTML = '';
data.arbitrage_opportunities.forEach(arb => {
const card = document.createElement('div');
card.className = 'p-4 rounded-xl border border-zinc-800 bg-zinc-950 space-y-2 font-mono';
card.innerHTML = `
<div class="flex items-center justify-between text-xs font-bold">
<span class="text-white">${arb.pair}</span>
<span class="px-2 py-0.5 rounded text-[10px] bg-sol-green/10 text-sol-green border border-sol-green/20">+${arb.spread_pct}% SPREAD</span>
</div>
<div class="text-[11px] text-zinc-400 space-y-1">
<div>Route: <span class="text-white">${arb.buy_venue} &rarr; ${arb.sell_venue}</span></div>
<div>Estimated Profit: <span class="text-sol-green font-bold">${arb.net_profit_est}</span></div>
</div>
`;
arbCont.appendChild(card);
});
}
} catch (err) {
console.error(err);
}
}
async function fetchTrades() {
try {
const res = await fetch('/api/trades');
const data = await res.json();
const container = document.getElementById('blotter-table');
container.innerHTML = '';
if (!data.trades || data.trades.length === 0) {
container.innerHTML = '<div class="p-6 text-center text-zinc-600">No trades executed yet.</div>';
return;
}
data.trades.forEach(t => {
const row = document.createElement('div');
row.className = 'grid grid-cols-12 p-3 items-center hover:bg-zinc-900/60 transition';
const sideColor = t.side === 'BUY' ? 'text-sol-green' : 'text-red-400';
row.innerHTML = `
<div class="col-span-3 text-zinc-400 font-bold">${t.trade_id}</div>
<div class="col-span-2 text-white font-bold">${t.symbol}/USDC</div>
<div class="col-span-2 ${sideColor} font-extrabold">${t.side}</div>
<div class="col-span-3 text-right text-white font-bold">$${t.amount_usdc.toLocaleString()}</div>
<div class="col-span-2 text-right text-sol-green font-bold text-[10px]">CONFIRMED</div>
`;
container.appendChild(row);
});
} catch (e) {
console.error(e);
}
}
async function submitTradeOrder() {
const sym = document.getElementById('trade-symbol').value;
const amt = parseFloat(document.getElementById('trade-amount').value);
if (!amt || amt <= 0) return;
const btn = document.getElementById('btn-submit-order');
btn.disabled = true;
btn.textContent = 'EXECUTING ON JUPITER...';
try {
const res = await fetch('/api/trades/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ symbol: sym, side: activeTradeSide, amount_usdc: amt })
});
const data = await res.json();
btn.textContent = 'ORDER CONFIRMED!';
setTimeout(() => {
btn.disabled = false;
btn.textContent = 'DISPATCH TO SOLANA';
}, 1200);
fetchMarket();
} catch (err) {
alert(err.message);
btn.disabled = false;
btn.textContent = 'DISPATCH TO SOLANA';
}
}
async function queryQuantAi() {
const prompt = document.getElementById('ai-prompt').value.trim();
if (!prompt) return;
const btn = document.getElementById('btn-run-ai');
btn.disabled = true;
btn.className = "px-6 py-2.5 rounded-lg bg-zinc-700 text-zinc-400 font-extrabold text-xs uppercase tracking-wider transition flex items-center space-x-2 cursor-not-allowed";
btn.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i><span>Analyzing...</span>';
initIcons();
const outBox = document.getElementById('ai-output-box');
outBox.textContent = '';
try {
const res = await fetch('/api/ai/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: prompt, max_tokens: 256 })
});
const reader = res.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const parts = buffer.split('\n\n');
buffer = parts.pop();
for (const p of parts) {
if (!p.trim()) continue;
let eventType = '';
let payload = '';
for (const line of p.split('\n')) {
if (line.startsWith('event: ')) eventType = line.substring(7).trim();
if (line.startsWith('data: ')) payload = line.substring(6).trim();
}
if (payload) {
const data = JSON.parse(payload);
if (eventType === 'token' && data.token) {
outBox.textContent += data.token;
outBox.scrollTop = outBox.scrollHeight;
}
}
}
}
} catch (err) {
outBox.textContent += '\n[Inference Error: ' + err.message + ']';
} finally {
btn.disabled = false;
btn.className = "px-6 py-2.5 rounded-lg bg-white hover:bg-zinc-200 text-black font-extrabold text-xs uppercase tracking-wider transition flex items-center space-x-2 shadow-lg active:scale-95";
btn.innerHTML = '<i data-lucide="zap" class="w-4 h-4 fill-black"></i><span>Run Analysis</span>';
initIcons();
}
}
function copyAiOutput() {
const text = document.getElementById('ai-output-box').textContent;
navigator.clipboard.writeText(text).then(() => {
const btn = document.getElementById('btn-copy-ai');
btn.textContent = 'COPIED';
setTimeout(() => { btn.textContent = 'COPY'; }, 1800);
});
}
window.addEventListener('DOMContentLoaded', () => {
initIcons();
fetchMarket();
});
</script>
</body>
</html>