120 lines
7.0 KiB
TypeScript
120 lines
7.0 KiB
TypeScript
import React from 'react';
|
|
import { PresetMarketplace } from '../components/PresetMarketplace';
|
|
import type { StrategyPreset } from '../lib/PresetRegistry';
|
|
import type { BotState } from '../hooks/useWebSocket';
|
|
import { TrendingUp, Brain } from 'lucide-react';
|
|
import { PageHeader } from '../components/ui/page-header';
|
|
import { Card, CardContent } from '../components/ui/card';
|
|
|
|
interface MarketplaceTabProps {
|
|
onClone: (preset: StrategyPreset) => void;
|
|
botState: BotState;
|
|
}
|
|
|
|
export const MarketplaceTab: React.FC<MarketplaceTabProps> = ({ onClone, botState }) => {
|
|
const symbols = Object.keys(botState.symbols);
|
|
|
|
const aiSetups = symbols
|
|
.filter(s => botState.symbols[s]?.rules?.['AIAnalysisRule']?.metadata?.confidence !== undefined)
|
|
.sort((a, b) => (botState.symbols[b]?.rules?.['AIAnalysisRule']?.metadata?.confidence || 0) - (botState.symbols[a]?.rules?.['AIAnalysisRule']?.metadata?.confidence || 0))
|
|
.slice(0, 5);
|
|
|
|
const topVolatile = [...symbols]
|
|
.sort((a, b) => Math.abs(botState.symbols[b].change24h || 0) - Math.abs(botState.symbols[a].change24h || 0))
|
|
.slice(0, 6);
|
|
|
|
return (
|
|
<div style={{ animation: 'fadeIn 0.5s ease-out' }}>
|
|
<PageHeader
|
|
title="Marketplace"
|
|
description="Explore curated strategy presets and compare them against current market context."
|
|
/>
|
|
|
|
{/* Contextual Intelligence Row */}
|
|
<div style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: '1fr 1fr',
|
|
gap: '20px',
|
|
maxWidth: '1400px',
|
|
margin: '24px auto 0 auto',
|
|
padding: '0 20px'
|
|
}}>
|
|
{/* AI Best Setups Panel */}
|
|
<Card className="hero-surface">
|
|
<CardContent style={{ padding: '24px 28px' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '20px' }}>
|
|
<div style={{ width: '32px', height: '32px', borderRadius: '10px', background: 'var(--accent-soft)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<Brain size={16} color="var(--accent)" />
|
|
</div>
|
|
<div>
|
|
<div className="section-title">Best AI Setups</div>
|
|
<div style={{ fontSize: '10px', color: 'var(--muted-foreground)', fontWeight: 700, marginTop: '1px' }}>High-confidence signals — find the right strategy below</div>
|
|
</div>
|
|
</div>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
|
{aiSetups.map(s => {
|
|
const conf = botState.symbols[s]?.rules['AIAnalysisRule']?.metadata?.confidence || 0;
|
|
return (
|
|
<div key={s} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 14px', background: 'var(--card-elevated)', borderRadius: '12px', border: '1px solid var(--border)' }}>
|
|
<span style={{ fontWeight: 800, fontSize: '13px', color: 'var(--foreground)' }}>{s}</span>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
<div style={{ width: `${Math.min(conf, 60)}px`, height: '3px', background: `hsl(${conf * 1.2}, 100%, 55%)`, borderRadius: '99px' }} />
|
|
<span style={{ fontSize: '12px', fontWeight: 900, color: 'var(--accent)', fontFamily: 'monospace' }}>{conf}%</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
{aiSetups.length === 0 && (
|
|
<div style={{ textAlign: 'center', padding: '20px', color: 'var(--muted-foreground)', fontSize: '12px', fontStyle: 'italic' }}>AI analysis scanning markets...</div>
|
|
)}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Top Volatile Panel */}
|
|
<Card className="hero-surface">
|
|
<CardContent style={{ padding: '24px 28px' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '20px' }}>
|
|
<div style={{ width: '32px', height: '32px', borderRadius: '10px', background: 'var(--bl-warning-muted)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<TrendingUp size={16} color="var(--bl-warning)" />
|
|
</div>
|
|
<div>
|
|
<div className="section-title">Top Volatile (24h)</div>
|
|
<div style={{ fontSize: '10px', color: 'var(--muted-foreground)', fontWeight: 700, marginTop: '1px' }}>Most active markets — match with strategies below</div>
|
|
</div>
|
|
</div>
|
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>
|
|
{topVolatile.map(s => {
|
|
const change = botState.symbols[s]?.change24h || 0;
|
|
return (
|
|
<div key={s} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 14px', background: 'var(--card-elevated)', borderRadius: '12px', border: '1px solid var(--border)' }}>
|
|
<span style={{ fontWeight: 800, fontSize: '12px', color: 'var(--foreground)' }}>{s.split('/')[0]}</span>
|
|
<span style={{ fontSize: '12px', fontWeight: 900, fontFamily: 'monospace', color: change >= 0 ? 'var(--bl-success)' : 'var(--bl-danger)' }}>
|
|
{change >= 0 ? '+' : ''}{change.toFixed(2)}%
|
|
</span>
|
|
</div>
|
|
);
|
|
})}
|
|
{topVolatile.length === 0 && (
|
|
<div style={{ gridColumn: '1/-1', textAlign: 'center', padding: '20px', color: 'var(--muted-foreground)', fontSize: '12px', fontStyle: 'italic' }}>Scanning markets...</div>
|
|
)}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Strategy Marketplace Grid */}
|
|
<div style={{ maxWidth: '1400px', margin: '0 auto', padding: '0 0 40px 0' }}>
|
|
<PresetMarketplace onSelect={onClone} />
|
|
</div>
|
|
|
|
<style>{`
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(12px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
};
|