1400 lines
72 KiB
React
1400 lines
72 KiB
React
import { useState, useEffect, useCallback } from "react";
|
||
import {
|
||
AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer,
|
||
BarChart, Bar, LineChart, Line, ReferenceLine
|
||
} from "recharts";
|
||
|
||
// ── Constants ─────────────────────────────────────────────────────────────────
|
||
const COINS = ["BTC","ETH","SOL","BNB","DOGE","AVAX","LINK","ARB","OP","APT",
|
||
"SUI","INJ","TIA","WLD","NEAR","FET","AAVE","DOT","ADA","XRP",
|
||
"LTC","BCH","CRV","ONDO","ENA","JUP","RENDER"];
|
||
const EXCHANGES = ["Bybit","Binance","OKX","Gate.io","KuCoin","Bitget","HTX","MEXC","BingX"];
|
||
const GRID_STRATEGIES = ["neutral","long","short","trend","reverse"];
|
||
const DCA_STRATEGIES = ["classic","aggressive","safe","trend","reverse"];
|
||
const REGIMES = ["BULL","BEAR","SIDEWAYS"];
|
||
|
||
// ── Palette ────────────────────────────────────────────────────────────────────
|
||
const C = {
|
||
bg:"#020b14", surface:"#05111f", card:"#071626",
|
||
border:"#0c2035", border2:"#102840",
|
||
accent:"#00d4aa", blue:"#1a8cff", purple:"#9b5de5",
|
||
danger:"#ff3358", warn:"#ffb700", success:"#00e676",
|
||
text:"#b8cfe8", muted:"#3a5872", dim:"#102030",
|
||
glow:"#00d4aa33",
|
||
};
|
||
|
||
// ── CSS ────────────────────────────────────────────────────────────────────────
|
||
const CSS = `
|
||
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Rajdhani:wght@300;400;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
body{background:${C.bg}}
|
||
::-webkit-scrollbar{width:3px;height:3px}
|
||
::-webkit-scrollbar-track{background:${C.bg}}
|
||
::-webkit-scrollbar-thumb{background:${C.border2};border-radius:2px}
|
||
@keyframes blink{0%,100%{opacity:1}50%{opacity:0.15}}
|
||
@keyframes pulse-ring{0%,100%{box-shadow:0 0 0 0 ${C.glow},0 0 8px ${C.accent}44}50%{box-shadow:0 0 0 6px transparent,0 0 20px ${C.accent}66}}
|
||
@keyframes slidein{from{transform:translateY(-6px);opacity:0}to{transform:translateY(0);opacity:1}}
|
||
@keyframes pump-flash{0%,100%{background:rgba(255,179,0,.06)}50%{background:rgba(255,179,0,.15)}}
|
||
@keyframes danger-flash{0%,100%{background:rgba(255,51,88,.06)}50%{background:rgba(255,51,88,.14)}}
|
||
@keyframes hum{0%{opacity:.6}50%{opacity:1}100%{opacity:.6}}
|
||
@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
|
||
@keyframes bar-grow{from{transform:scaleX(0)}to{transform:scaleX(1)}}
|
||
.live-dot{animation:blink 1.3s ease-in-out infinite}
|
||
.pulse{animation:pulse-ring 2.5s ease-in-out infinite}
|
||
.pump{animation:pump-flash 2s ease-in-out infinite}
|
||
.dump{animation:danger-flash 2s ease-in-out infinite}
|
||
.hum{animation:hum 3s ease-in-out infinite}
|
||
.spin{animation:spin 8s linear infinite}
|
||
.new-signal{animation:slidein .3s ease-out}
|
||
.nav-btn:hover{background:rgba(0,212,170,.07)!important;border-left-color:${C.accent}55!important}
|
||
input[type=range]{-webkit-appearance:none;appearance:none;height:3px;background:${C.dim};border-radius:2px;outline:none}
|
||
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;border-radius:50%;background:${C.accent};cursor:pointer;box-shadow:0 0 6px ${C.accent}99}
|
||
input[type=number]{background:${C.dim};border:1px solid ${C.border2};color:${C.text};padding:3px 8px;border-radius:4px;font-family:'JetBrains Mono';font-size:11px;width:70px;outline:none}
|
||
input[type=number]:focus{border-color:${C.accent}88}
|
||
select{background:${C.dim};border:1px solid ${C.border2};color:${C.text};padding:3px 8px;border-radius:4px;font-family:'JetBrains Mono';font-size:11px;outline:none;cursor:pointer}
|
||
select:focus{border-color:${C.accent}88}
|
||
button{cursor:pointer;font-family:'Rajdhani';letter-spacing:1px}
|
||
button:hover{filter:brightness(1.15)}
|
||
`;
|
||
|
||
// ── Helpers ────────────────────────────────────────────────────────────────────
|
||
const rand = (a,b) => Math.random()*(b-a)+a;
|
||
const pick = arr => arr[Math.floor(Math.random()*arr.length)];
|
||
const fmt2 = n => n.toFixed(2);
|
||
const fmtK = n => n >= 1000 ? `${(n/1000).toFixed(1)}K` : n.toFixed(0);
|
||
|
||
function genEquity(n=80){
|
||
let v=10000;
|
||
const now=Date.now();
|
||
return Array.from({length:n},(_,i)=>{
|
||
v*=(1+rand(-0.0045,0.006));
|
||
const d=new Date(now-(n-i)*3600000);
|
||
return{t:`${d.getMonth()+1}/${d.getDate()} ${d.getHours()}h`,v:Math.round(v*100)/100};
|
||
});
|
||
}
|
||
|
||
function genSignal(){
|
||
const coin=pick(COINS),dir=Math.random()>.5?"LONG":"SHORT";
|
||
const conf=+(rand(0.71,0.935)).toFixed(3);
|
||
return{
|
||
id:Date.now()+Math.random(),coin,dir,conf,
|
||
time:new Date().toLocaleTimeString("fr-FR",{hour:"2-digit",minute:"2-digit",second:"2-digit"}),
|
||
regime:pick(REGIMES),exec:conf>0.80
|
||
};
|
||
}
|
||
|
||
// ── Atom components ────────────────────────────────────────────────────────────
|
||
function Badge({text,color=C.accent,small}){
|
||
return(
|
||
<span style={{background:`${color}1a`,color,border:`1px solid ${color}44`,
|
||
borderRadius:3,padding:small?"1px 5px":"2px 7px",
|
||
fontSize:small?8:9,fontFamily:"JetBrains Mono",fontWeight:600,letterSpacing:.8,whiteSpace:"nowrap"}}>
|
||
{text}
|
||
</span>
|
||
);
|
||
}
|
||
|
||
function Toggle({on,toggle,label,color=C.accent}){
|
||
return(
|
||
<div onClick={toggle} style={{display:"flex",alignItems:"center",gap:10,cursor:"pointer",userSelect:"none"}}>
|
||
<div style={{width:38,height:20,borderRadius:10,
|
||
background:on?color:C.dim,position:"relative",
|
||
transition:"background .3s",border:`1px solid ${on?color:C.border2}`}}>
|
||
<div style={{position:"absolute",top:2,left:on?18:2,width:14,height:14,
|
||
borderRadius:7,background:"white",transition:"left .25s",
|
||
boxShadow:on?`0 0 6px ${color}99`:""}}/>
|
||
</div>
|
||
{label&&<span style={{color:on?color:C.muted,fontFamily:"Rajdhani",
|
||
fontSize:12,letterSpacing:.5,transition:"color .3s"}}>{label}</span>}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function KPI({label,value,sub,color=C.text,glow}){
|
||
return(
|
||
<div style={{background:C.card,border:`1px solid ${glow?color+"44":C.border}`,
|
||
borderRadius:8,padding:"14px 18px",
|
||
...(glow?{boxShadow:`0 0 16px ${color}22`}:{})}}>
|
||
<div style={{color:C.muted,fontSize:9,fontFamily:"Rajdhani",
|
||
letterSpacing:2.5,textTransform:"uppercase",marginBottom:5}}>{label}</div>
|
||
<div style={{color,fontSize:20,fontFamily:"JetBrains Mono",
|
||
fontWeight:600,lineHeight:1.1}}>{value}</div>
|
||
{sub&&<div style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani",marginTop:4}}>{sub}</div>}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function SectionTitle({children}){
|
||
return(
|
||
<div style={{color:C.muted,fontSize:9,fontFamily:"Rajdhani",
|
||
letterSpacing:2.5,textTransform:"uppercase",marginBottom:14,
|
||
display:"flex",alignItems:"center",gap:8}}>
|
||
<div style={{width:16,height:1,background:C.accent,opacity:.5}}/>
|
||
{children}
|
||
<div style={{flex:1,height:1,background:C.border}}/>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function Panel({children,style={},accentColor}){
|
||
return(
|
||
<div style={{background:C.card,border:`1px solid ${accentColor?accentColor+"33":C.border}`,
|
||
borderRadius:8,padding:18,...style}}>
|
||
{children}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function Row({l,v,color,mono=true}){
|
||
return(
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",
|
||
marginBottom:8,paddingBottom:7,borderBottom:`1px solid ${C.border}22`}}>
|
||
<span style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani",letterSpacing:.5}}>{l}</span>
|
||
<span style={{color:color||C.text,fontSize:11,fontFamily:mono?"JetBrains Mono":"Rajdhani"}}>{v}</span>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Confidence arc SVG
|
||
function ConfArc({value,size=140}){
|
||
const r=50,cx=size/2,cy=size/2;
|
||
const circ=2*Math.PI*r;
|
||
const arc=circ*0.75; // 270° arc
|
||
const filled=arc*value;
|
||
const color=value>0.85?C.success:value>0.72?C.accent:C.warn;
|
||
return(
|
||
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
||
<circle cx={cx} cy={cy} r={r} fill="none" stroke={C.dim} strokeWidth={7}
|
||
strokeDasharray={`${arc} ${circ}`}
|
||
strokeDashoffset={-circ*0.125}
|
||
strokeLinecap="round"/>
|
||
<circle cx={cx} cy={cy} r={r} fill="none" stroke={color} strokeWidth={7}
|
||
strokeDasharray={`${filled} ${circ-filled}`}
|
||
strokeDashoffset={-circ*0.125}
|
||
strokeLinecap="round"
|
||
style={{filter:`drop-shadow(0 0 6px ${color})`,transition:"stroke-dasharray .8s ease,stroke .5s"}}/>
|
||
<text x={cx} y={cy-6} textAnchor="middle" fill={C.text} fontSize={18}
|
||
fontFamily="JetBrains Mono" fontWeight="600">{(value*100).toFixed(1)}%</text>
|
||
<text x={cx} y={cy+12} textAnchor="middle" fill={C.muted} fontSize={8}
|
||
fontFamily="Rajdhani" letterSpacing={2}>CONFIDENCE</text>
|
||
<text x={cx} y={cy+24} textAnchor="middle"
|
||
fill={value>=0.72?C.success:C.warn} fontSize={8} fontFamily="Rajdhani">
|
||
{value>=0.72?"▶ SIGNAL VALID":"⏸ BELOW THRESHOLD"}
|
||
</text>
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
// Signal feed row
|
||
function SigRow({s}){
|
||
const isL=s.dir==="LONG",col=isL?C.success:C.danger;
|
||
return(
|
||
<div className="new-signal" style={{
|
||
display:"grid",gridTemplateColumns:"54px 46px 90px 60px 1fr 52px",
|
||
alignItems:"center",gap:6,
|
||
padding:"5px 10px",borderBottom:`1px solid ${C.border}18`,
|
||
background:s.exec?`${col}08`:"transparent"}}>
|
||
<span style={{color:C.text,fontFamily:"JetBrains Mono",fontSize:11,fontWeight:600}}>{s.coin}</span>
|
||
<Badge text={s.dir} color={col} small/>
|
||
<div style={{display:"flex",alignItems:"center",gap:4}}>
|
||
<div style={{flex:1,height:2,background:C.dim,borderRadius:2}}>
|
||
<div style={{width:`${s.conf*100}%`,height:"100%",
|
||
background:s.conf>0.85?C.success:C.accent,borderRadius:2,transition:"width .5s"}}/>
|
||
</div>
|
||
<span style={{color:C.muted,fontSize:8,fontFamily:"JetBrains Mono"}}>{(s.conf*100).toFixed(1)}%</span>
|
||
</div>
|
||
<Badge text={s.regime} small
|
||
color={s.regime==="BULL"?C.success:s.regime==="BEAR"?C.danger:C.warn}/>
|
||
<span style={{color:C.muted,fontSize:9,fontFamily:"JetBrains Mono"}}>{s.time}</span>
|
||
<span style={{color:s.exec?C.success:C.muted,fontSize:8,fontFamily:"Rajdhani",
|
||
letterSpacing:.5,textAlign:"right"}}>{s.exec?"▶ EXEC":"— SKIP"}</span>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Position card
|
||
function PosCard({p,onClose}){
|
||
const isL=p.dir==="LONG",col=isL?C.success:C.danger;
|
||
const pnlColor=p.pnl>=0?C.success:C.danger;
|
||
return(
|
||
<div style={{border:`1px solid ${col}28`,borderRadius:7,padding:12,
|
||
marginBottom:8,background:`${col}05`}}>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:8}}>
|
||
<div style={{display:"flex",gap:8,alignItems:"center"}}>
|
||
<span style={{color:C.text,fontFamily:"Orbitron",fontSize:13,fontWeight:700}}>{p.coin}</span>
|
||
<Badge text={p.dir} color={col}/>
|
||
<span style={{color:C.muted,fontFamily:"Rajdhani",fontSize:10}}>{p.age}</span>
|
||
</div>
|
||
<div style={{display:"flex",alignItems:"center",gap:10}}>
|
||
<span style={{color:pnlColor,fontFamily:"JetBrains Mono",fontSize:14,fontWeight:600}}>
|
||
{p.pnl>=0?"+":""}{fmt2(p.pnl)} USDT
|
||
</span>
|
||
{onClose&&<button onClick={()=>onClose(p.coin)}
|
||
style={{background:`${C.danger}18`,border:`1px solid ${C.danger}44`,
|
||
color:C.danger,padding:"3px 9px",borderRadius:4,fontSize:10}}>
|
||
CLOSE
|
||
</button>}
|
||
</div>
|
||
</div>
|
||
<div style={{display:"grid",gridTemplateColumns:"repeat(4,1fr)",gap:6,marginBottom:6}}>
|
||
{[["Entry",`$${p.entry.toLocaleString()}`],["Current",`$${p.current.toLocaleString()}`],
|
||
["SL",`$${p.sl.toLocaleString()}`],["TP",`$${p.tp.toLocaleString()}`]].map(([l,v])=>(
|
||
<div key={l}>
|
||
<div style={{color:C.muted,fontSize:7,fontFamily:"Rajdhani",letterSpacing:1.5,marginBottom:1}}>{l}</div>
|
||
<div style={{color:l==="SL"?C.danger:l==="TP"?C.success:C.text,fontSize:10,fontFamily:"JetBrains Mono"}}>{v}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{display:"flex",alignItems:"center",gap:6}}>
|
||
<span style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",letterSpacing:1}}>CONF</span>
|
||
<div style={{flex:1,height:2,background:C.dim,borderRadius:2}}>
|
||
<div style={{width:`${p.conf*100}%`,height:"100%",background:C.accent,borderRadius:2}}/>
|
||
</div>
|
||
<span style={{color:C.muted,fontSize:8,fontFamily:"JetBrains Mono"}}>{(p.conf*100).toFixed(0)}%</span>
|
||
<span style={{color:C.muted,fontSize:8,fontFamily:"JetBrains Mono",marginLeft:6}}>
|
||
Lev {p.lev}x
|
||
</span>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Main Component ─────────────────────────────────────────────────────────────
|
||
export default function AHAD QUANT(){
|
||
const [tab,setTab]=useState("overview");
|
||
const [clock,setClock]=useState(new Date());
|
||
|
||
// Bot toggles
|
||
const [botOn,setBotOn]=useState(true);
|
||
const [gridOn,setGridOn]=useState(false);
|
||
const [dcaOn,setDcaOn]=useState(false);
|
||
const [pumpOn,setPumpOn]=useState(true);
|
||
const [paperMode,setPaperMode]=useState(true);
|
||
const [exchange,setExchange]=useState("Bybit");
|
||
const [retrainOn,setRetrainOn]=useState(false);
|
||
const [regimeFilter,setRegimeFilter]=useState(false);
|
||
|
||
// Config
|
||
const [cfg,setCfg]=useState({
|
||
leverage:5,risk:3,sl:1.5,tp:2.2,maxPos:5,maxLoss:5,minConf:72,
|
||
cbLosses:3,gridLevels:10,gridUsdt:200,gridCoin:"BTC",gridStrategy:"neutral",
|
||
dcaBase:50,dcaSafety:30,dcaMaxSafety:5,dcaDev:1.5,dcaTp:2,dcaCoin:"BTC",
|
||
pumpVol:5,pumpPrice:3,pumpLev:5,pumpRisk:5,
|
||
retrainInterval:24,retrainMinAcc:55,
|
||
maxHold:3,compound:true
|
||
});
|
||
const setC=useCallback((k,v)=>setCfg(p=>({...p,[k]:v})),[]);
|
||
|
||
// Live data
|
||
const [balance,setBalance]=useState(10847.23);
|
||
const [dailyPnL,setDailyPnL]=useState(234.56);
|
||
const [equity,setEquity]=useState(()=>genEquity());
|
||
const [signals,setSignals]=useState(()=>[...Array(6)].map(genSignal));
|
||
const [positions,setPositions]=useState([
|
||
{coin:"BTC",dir:"LONG",entry:67420,current:67892,size:0.015,
|
||
pnl:45.23,conf:0.847,age:"2h 14m",sl:66408,tp:68904,lev:5},
|
||
{coin:"ETH",dir:"SHORT",entry:3521.4,current:3534.2,size:0.12,
|
||
pnl:-12.45,conf:0.781,age:"45m",sl:3573.6,tp:3443.2,lev:5},
|
||
]);
|
||
const [regime,setRegime]=useState("BULL");
|
||
const [confidence,setConfidence]=useState(0.847);
|
||
const [cbLosses,setCbLosses]=useState(1);
|
||
const [winRate,setWinRate]=useState(74.6);
|
||
const [totalTrades,setTotalTrades]=useState(312);
|
||
const [accuracy,setAccuracy]=useState(74.6);
|
||
const [pumpAlerts,setPumpAlerts]=useState([
|
||
{coin:"SUI",type:"PUMP",pct:"+8.3%",vol:"12.4x",time:"2m ago",act:true},
|
||
{coin:"INJ",type:"PUMP",pct:"+5.1%",vol:"6.8x",time:"8m ago",act:true},
|
||
{coin:"DOGE",type:"DUMP",pct:"-4.2%",vol:"3.1x",time:"23m ago",act:false},
|
||
]);
|
||
|
||
// Clock
|
||
useEffect(()=>{
|
||
const iv=setInterval(()=>setClock(new Date()),1000);
|
||
return()=>clearInterval(iv);
|
||
},[]);
|
||
|
||
// Live simulation
|
||
useEffect(()=>{
|
||
const iv=setInterval(()=>{
|
||
setBalance(b=>+Math.max(9000,b+rand(-8,14)).toFixed(2));
|
||
setDailyPnL(p=>+(p+rand(-4,5.5)).toFixed(2));
|
||
setConfidence(+(rand(0.71,0.94)).toFixed(3));
|
||
setPositions(prev=>prev.map(p=>({
|
||
...p,
|
||
current:+(p.current*(1+rand(-0.0006,0.0007))).toFixed(p.coin==="BTC"?0:1),
|
||
pnl:+(p.pnl+rand(-2.5,3)).toFixed(2),
|
||
})));
|
||
setEquity(prev=>{
|
||
const last=prev[prev.length-1];
|
||
const nv=Math.max(9200,last.v*(1+rand(-0.0012,0.0017)));
|
||
const d=new Date();
|
||
return[...prev.slice(-79),{t:`${d.getHours()}:${String(d.getMinutes()).padStart(2,"0")}`,v:Math.round(nv*100)/100}];
|
||
});
|
||
if(Math.random()>.5)setSignals(prev=>[genSignal(),...prev.slice(0,29)]);
|
||
if(Math.random()>.85)setRegime(pick(REGIMES));
|
||
},1800);
|
||
return()=>clearInterval(iv);
|
||
},[]);
|
||
|
||
const closePos=useCallback((coin)=>{
|
||
setPositions(p=>p.filter(x=>x.coin!==coin));
|
||
},[]);
|
||
|
||
// Derived
|
||
const totalPnL=equity.length?equity[equity.length-1].v-10000:0;
|
||
const dailyColor=dailyPnL>=0?C.success:C.danger;
|
||
const regimeColor=regime==="BULL"?C.success:regime==="BEAR"?C.danger:C.warn;
|
||
const regimeIcon=regime==="BULL"?"↗":regime==="BEAR"?"↘":"→";
|
||
|
||
// ── Tab: Overview ──────────────────────────────────────────────────────────
|
||
function TabOverview(){
|
||
return(
|
||
<div style={{display:"flex",flexDirection:"column",gap:14}}>
|
||
{/* KPIs */}
|
||
<div style={{display:"grid",gridTemplateColumns:"repeat(4,1fr)",gap:12}}>
|
||
<KPI label="Portfolio Balance" glow
|
||
value={`$${balance.toLocaleString("en-US",{minimumFractionDigits:2})}`}
|
||
sub={paperMode?"📄 Paper Mode":"💰 Live Trading"} color={C.accent}/>
|
||
<KPI label="Daily P&L"
|
||
value={`${dailyPnL>=0?"+":""}$${fmt2(dailyPnL)}`}
|
||
sub={`${(dailyPnL/balance*100).toFixed(2)}% today`} color={dailyColor}/>
|
||
<KPI label="Total Profit"
|
||
value={`${totalPnL>=0?"+":""}$${Math.round(totalPnL).toLocaleString()}`}
|
||
sub={`${((totalPnL/10000)*100).toFixed(2)}% from start`}
|
||
color={totalPnL>=0?C.success:C.danger}/>
|
||
<KPI label="Win Rate · Accuracy"
|
||
value={`${winRate.toFixed(1)}%`}
|
||
sub={`AI: ${accuracy.toFixed(1)}% · ${totalTrades} trades`} color={C.blue}/>
|
||
</div>
|
||
|
||
{/* Equity + Regime + Status */}
|
||
<div style={{display:"grid",gridTemplateColumns:"1fr 220px",gap:12}}>
|
||
<Panel>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:12}}>
|
||
<SectionTitle>EQUITY CURVE · 80H</SectionTitle>
|
||
<span style={{color:totalPnL>=0?C.success:C.danger,fontFamily:"JetBrains Mono",fontSize:12}}>
|
||
{totalPnL>=0?"+":""}${totalPnL.toFixed(2)} ({((totalPnL/10000)*100).toFixed(2)}%)
|
||
</span>
|
||
</div>
|
||
<ResponsiveContainer width="100%" height={150}>
|
||
<AreaChart data={equity} margin={{top:0,right:0,left:-28,bottom:0}}>
|
||
<defs>
|
||
<linearGradient id="gEq" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="5%" stopColor={C.accent} stopOpacity={.35}/>
|
||
<stop offset="95%" stopColor={C.accent} stopOpacity={0}/>
|
||
</linearGradient>
|
||
</defs>
|
||
<XAxis dataKey="t" tick={{fill:C.muted,fontSize:7,fontFamily:"JetBrains Mono"}}
|
||
tickLine={false} axisLine={false} interval={15}/>
|
||
<YAxis tick={{fill:C.muted,fontSize:7,fontFamily:"JetBrains Mono"}}
|
||
tickLine={false} axisLine={false}/>
|
||
<Tooltip contentStyle={{background:C.surface,border:`1px solid ${C.accent}44`,
|
||
borderRadius:6,fontFamily:"JetBrains Mono",fontSize:10,color:C.text}}/>
|
||
<ReferenceLine y={10000} stroke={C.muted} strokeDasharray="3 3" strokeOpacity={.4}/>
|
||
<Area type="monotone" dataKey="v" stroke={C.accent} strokeWidth={1.5}
|
||
fill="url(#gEq)" dot={false}/>
|
||
</AreaChart>
|
||
</ResponsiveContainer>
|
||
</Panel>
|
||
|
||
<div style={{display:"flex",flexDirection:"column",gap:12}}>
|
||
{/* Regime */}
|
||
<Panel style={{flex:1,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"}}>
|
||
<div style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",letterSpacing:2.5,marginBottom:12}}>MARKET REGIME · HMM</div>
|
||
<div className="pulse" style={{width:72,height:72,borderRadius:"50%",
|
||
border:`2.5px solid ${regimeColor}`,display:"flex",alignItems:"center",
|
||
justifyContent:"center",marginBottom:10}}>
|
||
<span style={{fontSize:28}}>{regimeIcon}</span>
|
||
</div>
|
||
<span style={{color:regimeColor,fontFamily:"Orbitron",fontSize:15,
|
||
fontWeight:700,letterSpacing:3}}>{regime}</span>
|
||
<span style={{color:C.muted,fontSize:8,fontFamily:"JetBrains Mono",marginTop:4}}>
|
||
{regimeFilter?"Filter: ON":"Filter: OFF"}
|
||
</span>
|
||
</Panel>
|
||
|
||
{/* Bot Status */}
|
||
<Panel>
|
||
<div style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",
|
||
letterSpacing:2.5,marginBottom:10}}>MODULE STATUS</div>
|
||
{[
|
||
{l:"AI Bot",on:botOn,color:C.accent},
|
||
{l:"Grid Bot",on:gridOn,color:C.blue},
|
||
{l:"DCA Bot",on:dcaOn,color:C.warn},
|
||
{l:"Pump Scanner",on:pumpOn,color:C.success},
|
||
{l:"Auto-Retrain",on:retrainOn,color:C.purple},
|
||
].map(({l,on,color})=>(
|
||
<div key={l} style={{display:"flex",justifyContent:"space-between",
|
||
alignItems:"center",marginBottom:7}}>
|
||
<span style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani"}}>{l}</span>
|
||
<div style={{display:"flex",alignItems:"center",gap:5}}>
|
||
<div className={on?"live-dot":""} style={{width:5,height:5,borderRadius:"50%",
|
||
background:on?color:C.dim,...(on?{boxShadow:`0 0 5px ${color}`}:{})}}/>
|
||
<span style={{color:on?color:C.muted,fontSize:8,fontFamily:"JetBrains Mono"}}>
|
||
{on?"RUN":"OFF"}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</Panel>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Positions + Signals */}
|
||
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12}}>
|
||
<Panel>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:12}}>
|
||
<SectionTitle>OPEN POSITIONS · {positions.length}/{cfg.maxPos}</SectionTitle>
|
||
<button onClick={()=>setPositions([])}
|
||
style={{background:`${C.danger}15`,border:`1px solid ${C.danger}44`,
|
||
color:C.danger,padding:"4px 12px",borderRadius:4,fontSize:10}}>
|
||
CLOSE ALL
|
||
</button>
|
||
</div>
|
||
{positions.length===0?(
|
||
<div style={{color:C.muted,textAlign:"center",padding:30,
|
||
fontFamily:"JetBrains Mono",fontSize:11}}>No active positions</div>
|
||
):positions.map(p=><PosCard key={p.coin} p={p} onClose={closePos}/>)}
|
||
</Panel>
|
||
|
||
<Panel>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:12}}>
|
||
<SectionTitle>SIGNAL STREAM · LIVE</SectionTitle>
|
||
<div style={{display:"flex",alignItems:"center",gap:5}}>
|
||
<div className="live-dot" style={{width:5,height:5,borderRadius:"50%",
|
||
background:C.success,boxShadow:`0 0 5px ${C.success}`}}/>
|
||
<span style={{color:C.success,fontSize:8,fontFamily:"JetBrains Mono"}}>STREAMING</span>
|
||
</div>
|
||
</div>
|
||
<div style={{display:"grid",gridTemplateColumns:"54px 46px 90px 60px 1fr 52px",
|
||
gap:6,marginBottom:6,padding:"0 10px"}}>
|
||
{["COIN","DIR","CONF","REGIME","TIME","STATUS"].map(h=>(
|
||
<span key={h} style={{color:C.muted,fontSize:7,fontFamily:"Rajdhani",letterSpacing:1.5}}>{h}</span>
|
||
))}
|
||
</div>
|
||
<div style={{maxHeight:210,overflowY:"auto"}}>
|
||
{signals.map(s=><SigRow key={s.id} s={s}/>)}
|
||
</div>
|
||
</Panel>
|
||
</div>
|
||
|
||
{/* Pump + Risk preview */}
|
||
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12}}>
|
||
<Panel>
|
||
<SectionTitle>PUMP SCANNER · ALERTS</SectionTitle>
|
||
{pumpAlerts.map((a,i)=>(
|
||
<div key={i} className={a.act?(a.type==="PUMP"?"pump":"dump"):""}
|
||
style={{display:"flex",justifyContent:"space-between",alignItems:"center",
|
||
padding:"9px 12px",borderRadius:6,marginBottom:6,
|
||
border:`1px solid ${a.act?(a.type==="PUMP"?C.warn:C.danger):C.border}44`}}>
|
||
<div style={{display:"flex",gap:10,alignItems:"center"}}>
|
||
<span style={{color:C.text,fontFamily:"Orbitron",fontSize:12,fontWeight:700}}>{a.coin}</span>
|
||
<Badge text={a.type} color={a.type==="PUMP"?C.warn:C.danger}/>
|
||
<span style={{color:a.type==="PUMP"?C.warn:C.danger,fontFamily:"JetBrains Mono",fontSize:14,fontWeight:600}}>{a.pct}</span>
|
||
</div>
|
||
<div style={{textAlign:"right"}}>
|
||
<div style={{color:C.text,fontSize:10,fontFamily:"JetBrains Mono"}}>Vol: {a.vol}</div>
|
||
<div style={{color:C.muted,fontSize:9,fontFamily:"JetBrains Mono"}}>{a.time}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</Panel>
|
||
|
||
<Panel>
|
||
<SectionTitle>RISK MANAGER</SectionTitle>
|
||
{/* Circuit breaker */}
|
||
<div style={{marginBottom:14}}>
|
||
<div style={{display:"flex",justifyContent:"space-between",marginBottom:6}}>
|
||
<span style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani"}}>Circuit Breaker</span>
|
||
<span style={{color:cbLosses>=cfg.cbLosses?C.danger:C.warn,
|
||
fontFamily:"JetBrains Mono",fontSize:10}}>
|
||
{cbLosses}/{cfg.cbLosses} losses
|
||
</span>
|
||
</div>
|
||
<div style={{display:"flex",gap:4}}>
|
||
{[...Array(cfg.cbLosses)].map((_,i)=>(
|
||
<div key={i} style={{flex:1,height:6,borderRadius:3,
|
||
background:i<cbLosses?`${C.danger}cc`:C.dim,transition:"background .3s"}}/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
{/* Daily loss */}
|
||
{(()=>{
|
||
const used=Math.max(0,Math.abs(Math.min(0,dailyPnL))/balance*100);
|
||
const pct=Math.min(used/cfg.maxLoss*100,100);
|
||
return(
|
||
<div style={{marginBottom:14}}>
|
||
<div style={{display:"flex",justifyContent:"space-between",marginBottom:6}}>
|
||
<span style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani"}}>Daily Loss Used</span>
|
||
<span style={{color:pct>75?C.danger:C.text,fontFamily:"JetBrains Mono",fontSize:10}}>
|
||
{used.toFixed(2)}% / {cfg.maxLoss}%
|
||
</span>
|
||
</div>
|
||
<div style={{height:5,background:C.dim,borderRadius:3,overflow:"hidden"}}>
|
||
<div style={{width:`${pct}%`,height:"100%",borderRadius:3,
|
||
background:pct>75?C.danger:pct>50?C.warn:C.success,transition:"width .5s"}}/>
|
||
</div>
|
||
</div>
|
||
);
|
||
})()}
|
||
<Row l="Risk/Trade" v={`${cfg.risk}% of equity`}/>
|
||
<Row l="SL / TP" v={`${cfg.sl}% / ${cfg.tp}%`}/>
|
||
<Row l="Leverage" v={`${cfg.leverage}x`}/>
|
||
<Row l="Min Confidence" v={`${cfg.minConf}%`}/>
|
||
</Panel>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Tab: AI Bot ────────────────────────────────────────────────────────────
|
||
function TabAIBot(){
|
||
return(
|
||
<div style={{display:"flex",flexDirection:"column",gap:14}}>
|
||
<div style={{display:"grid",gridTemplateColumns:"260px 1fr",gap:14}}>
|
||
{/* Left: controls + confidence */}
|
||
<div style={{display:"flex",flexDirection:"column",gap:12}}>
|
||
<Panel accentColor={C.accent}>
|
||
<SectionTitle>AI BOT CONTROL</SectionTitle>
|
||
<div style={{display:"flex",justifyContent:"center",marginBottom:16}}>
|
||
<ConfArc value={confidence}/>
|
||
</div>
|
||
<div style={{display:"flex",flexDirection:"column",gap:10,marginBottom:14}}>
|
||
<Toggle on={botOn} toggle={()=>setBotOn(!botOn)} label="Bot Active" color={C.accent}/>
|
||
<Toggle on={paperMode} toggle={()=>setPaperMode(!paperMode)}
|
||
label={paperMode?"Paper Mode":"Live Mode"} color={paperMode?C.blue:C.danger}/>
|
||
<Toggle on={regimeFilter} toggle={()=>setRegimeFilter(!regimeFilter)}
|
||
label="Regime Filter (HMM)" color={C.purple}/>
|
||
<Toggle on={retrainOn} toggle={()=>setRetrainOn(!retrainOn)}
|
||
label="Auto-Retrain" color={C.purple}/>
|
||
</div>
|
||
<div style={{borderTop:`1px solid ${C.border}`,paddingTop:12}}>
|
||
<Row l="Model" v="Ensemble"/>
|
||
<Row l="Components" v="LGB+XGB+RF"/>
|
||
<Row l="Features" v="62 engineered"/>
|
||
<Row l="Min Confidence" v={`${cfg.minConf}%`}/>
|
||
<Row l="Accuracy" v={`${accuracy.toFixed(1)}%`} color={C.blue}/>
|
||
<Row l="Win Rate" v={`${winRate.toFixed(1)}%`} color={winRate>60?C.success:C.warn}/>
|
||
</div>
|
||
<div style={{display:"flex",gap:8,marginTop:12}}>
|
||
<button style={{flex:1,background:`${C.danger}15`,border:`1px solid ${C.danger}44`,
|
||
color:C.danger,padding:"7px 0",borderRadius:5,fontSize:11}}>
|
||
STOP BOT
|
||
</button>
|
||
<button style={{flex:1,background:`${C.accent}15`,border:`1px solid ${C.accent}44`,
|
||
color:C.accent,padding:"7px 0",borderRadius:5,fontSize:11}}>
|
||
RESTART
|
||
</button>
|
||
</div>
|
||
</Panel>
|
||
</div>
|
||
|
||
{/* Right: positions + signal stream */}
|
||
<div style={{display:"flex",flexDirection:"column",gap:12}}>
|
||
<Panel>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:12}}>
|
||
<SectionTitle>OPEN POSITIONS · {positions.length}/{cfg.maxPos}</SectionTitle>
|
||
<button onClick={()=>setPositions([])}
|
||
style={{background:`${C.danger}15`,border:`1px solid ${C.danger}44`,
|
||
color:C.danger,padding:"4px 12px",borderRadius:4,fontSize:10}}>
|
||
CLOSE ALL
|
||
</button>
|
||
</div>
|
||
{positions.length===0
|
||
?<div style={{color:C.muted,textAlign:"center",padding:24,fontFamily:"JetBrains Mono",fontSize:11}}>
|
||
No active positions
|
||
</div>
|
||
:positions.map(p=><PosCard key={p.coin} p={p} onClose={closePos}/>)}
|
||
</Panel>
|
||
|
||
<Panel style={{flex:1}}>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:12}}>
|
||
<SectionTitle>LIVE SIGNAL STREAM</SectionTitle>
|
||
<div style={{display:"flex",alignItems:"center",gap:5}}>
|
||
<div className="live-dot" style={{width:5,height:5,borderRadius:"50%",
|
||
background:botOn?C.success:C.dim,...(botOn?{boxShadow:`0 0 5px ${C.success}`}:{})}}/>
|
||
<span style={{color:botOn?C.success:C.muted,fontSize:8,fontFamily:"JetBrains Mono"}}>
|
||
{botOn?"LIVE":"PAUSED"}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div style={{display:"grid",gridTemplateColumns:"54px 46px 90px 60px 1fr 52px",
|
||
gap:6,marginBottom:6,padding:"0 10px"}}>
|
||
{["COIN","DIR","CONF","REGIME","TIME","STATUS"].map(h=>(
|
||
<span key={h} style={{color:C.muted,fontSize:7,fontFamily:"Rajdhani",letterSpacing:1.5}}>{h}</span>
|
||
))}
|
||
</div>
|
||
<div style={{maxHeight:240,overflowY:"auto"}}>
|
||
{signals.map(s=><SigRow key={s.id} s={s}/>)}
|
||
</div>
|
||
</Panel>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Tab: Grid Bot ──────────────────────────────────────────────────────────
|
||
function TabGrid(){
|
||
const lower=65000,upper=70000,cur=67892,levels=cfg.gridLevels;
|
||
const step=(upper-lower)/levels;
|
||
const gridLines=[...Array(levels+1)].map((_,i)=>lower+i*step);
|
||
const gridPnL=12.4,filledCount=4;
|
||
return(
|
||
<div style={{display:"grid",gridTemplateColumns:"280px 1fr",gap:14}}>
|
||
<div style={{display:"flex",flexDirection:"column",gap:12}}>
|
||
<Panel accentColor={C.blue}>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16}}>
|
||
<SectionTitle>GRID BOT</SectionTitle>
|
||
<Toggle on={gridOn} toggle={()=>setGridOn(!gridOn)} color={C.blue}/>
|
||
</div>
|
||
<Row l="Coin" v={cfg.gridCoin+"/USDT"}/>
|
||
<Row l="Strategy" v={cfg.gridStrategy}/>
|
||
<Row l="Range" v={`$${(lower/1000).toFixed(0)}K – $${(upper/1000).toFixed(0)}K`}/>
|
||
<Row l="Grid Levels" v={cfg.gridLevels}/>
|
||
<Row l="Total USDT" v={`$${cfg.gridUsdt}`}/>
|
||
<Row l="Per Grid" v={`$${(cfg.gridUsdt/cfg.gridLevels).toFixed(1)}`}/>
|
||
<Row l="Leverage" v="3x"/>
|
||
<div style={{borderTop:`1px solid ${C.border}`,marginTop:10,paddingTop:10}}>
|
||
<div style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",letterSpacing:2,marginBottom:8}}>RUNTIME STATS</div>
|
||
<Row l="Filled Orders" v={`${filledCount} / ${levels}`} color={C.success}/>
|
||
<Row l="Grid P&L" v={`+$${gridPnL.toFixed(2)}`} color={C.success}/>
|
||
<Row l="Est. Daily" v="~1.2%"/>
|
||
</div>
|
||
<div style={{marginTop:12}}>
|
||
<div style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",letterSpacing:1.5,marginBottom:8}}>STRATEGY</div>
|
||
<div style={{display:"flex",flexWrap:"wrap",gap:5}}>
|
||
{GRID_STRATEGIES.map(s=>(
|
||
<button key={s} onClick={()=>setC("gridStrategy",s)}
|
||
style={{background:cfg.gridStrategy===s?`${C.blue}22`:C.dim,
|
||
border:`1px solid ${cfg.gridStrategy===s?C.blue:C.border}`,
|
||
color:cfg.gridStrategy===s?C.blue:C.muted,
|
||
padding:"3px 10px",borderRadius:4,fontSize:10}}>
|
||
{s}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</Panel>
|
||
</div>
|
||
|
||
<Panel>
|
||
<SectionTitle>GRID VISUALIZATION · {cfg.gridCoin}/USDT</SectionTitle>
|
||
<div style={{position:"relative",height:380}}>
|
||
{gridLines.map((price,i)=>{
|
||
const pct=(price-lower)/(upper-lower)*100;
|
||
const above=price>cur;
|
||
const filled=!above&&i%3===0;
|
||
return(
|
||
<div key={i} style={{position:"absolute",left:0,right:0,bottom:`${pct}%`,
|
||
display:"flex",alignItems:"center",gap:8}}>
|
||
<div style={{flex:1,height:1,
|
||
background:filled?`${C.success}55`:above?`${C.danger}22`:`${C.border}55`}}/>
|
||
<div style={{display:"flex",alignItems:"center",gap:6}}>
|
||
{filled&&<Badge text="FILLED" color={C.success} small/>}
|
||
{above&&<Badge text="SELL" color={C.danger} small/>}
|
||
{!filled&&!above&&<Badge text="BUY" color={C.success} small/>}
|
||
<span style={{color:filled?C.success:above?C.danger:C.muted,
|
||
fontSize:9,fontFamily:"JetBrains Mono",width:80,textAlign:"right"}}>
|
||
${price.toLocaleString()}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
{/* Current price */}
|
||
<div style={{position:"absolute",left:0,right:0,
|
||
bottom:`${(cur-lower)/(upper-lower)*100}%`,
|
||
display:"flex",alignItems:"center",gap:8}}>
|
||
<div style={{flex:1,height:2,background:C.accent,
|
||
boxShadow:`0 0 8px ${C.accent}`}}/>
|
||
<div style={{display:"flex",alignItems:"center",gap:6}}>
|
||
<div className="hum" style={{background:`${C.accent}22`,border:`1px solid ${C.accent}`,
|
||
borderRadius:3,padding:"2px 8px"}}>
|
||
<span style={{color:C.accent,fontSize:10,fontFamily:"JetBrains Mono",fontWeight:600}}>
|
||
CURRENT ${cur.toLocaleString()} ◄
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Panel>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Tab: DCA Bot ───────────────────────────────────────────────────────────
|
||
function TabDCA(){
|
||
const safeties=[...Array(cfg.dcaMaxSafety)].map((_,i)=>({
|
||
lv:i+1,
|
||
price:Math.round(67420*(1-cfg.dcaMaxSafety*cfg.dcaDev/100+i*cfg.dcaDev/100)),
|
||
size:+(cfg.dcaSafety*Math.pow(1.5,i)).toFixed(0),
|
||
filled:i<2
|
||
}));
|
||
const totalInvested=cfg.dcaBase+safeties.filter(s=>s.filled).reduce((a,b)=>a+b.size,0);
|
||
return(
|
||
<div style={{display:"grid",gridTemplateColumns:"280px 1fr",gap:14}}>
|
||
<Panel accentColor={C.warn}>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16}}>
|
||
<SectionTitle>DCA BOT</SectionTitle>
|
||
<Toggle on={dcaOn} toggle={()=>setDcaOn(!dcaOn)} color={C.warn}/>
|
||
</div>
|
||
<Row l="Coin" v={cfg.dcaCoin+"/USDT"}/>
|
||
<Row l="Strategy" v="Classic"/>
|
||
<Row l="Base Order" v={`$${cfg.dcaBase}`}/>
|
||
<Row l="Safety Order" v={`$${cfg.dcaSafety}`}/>
|
||
<Row l="Max Safety Orders" v={cfg.dcaMaxSafety}/>
|
||
<Row l="Price Deviation" v={`${cfg.dcaDev}%`}/>
|
||
<Row l="Take Profit" v={`${cfg.dcaTp}%`}/>
|
||
<div style={{borderTop:`1px solid ${C.border}`,marginTop:10,paddingTop:10}}>
|
||
<Row l="Total Invested" v={`$${totalInvested.toFixed(0)}`} color={C.warn}/>
|
||
<Row l="Safety Filled" v={`${safeties.filter(s=>s.filled).length}/${cfg.dcaMaxSafety}`} color={C.warn}/>
|
||
<Row l="Avg Entry" v="$66,405"/>
|
||
<Row l="Current PnL" v="+$8.20" color={C.success}/>
|
||
</div>
|
||
<div style={{marginTop:12}}>
|
||
<div style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",letterSpacing:1.5,marginBottom:8}}>STRATEGY</div>
|
||
<div style={{display:"flex",flexWrap:"wrap",gap:5}}>
|
||
{DCA_STRATEGIES.map(s=>(
|
||
<button key={s} onClick={()=>setC("dcaStrategy",s)}
|
||
style={{background:C.dim,border:`1px solid ${C.border}`,
|
||
color:C.muted,padding:"3px 10px",borderRadius:4,fontSize:10}}>
|
||
{s}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</Panel>
|
||
|
||
<Panel>
|
||
<SectionTitle>ORDER LADDER · {cfg.dcaCoin}/USDT</SectionTitle>
|
||
{/* Header */}
|
||
<div style={{display:"grid",gridTemplateColumns:"55px 110px 90px 100px 90px 70px",
|
||
gap:0,marginBottom:8,padding:"0 4px"}}>
|
||
{["LEVEL","PRICE","SIZE (USDT)","CUM. SIZE","DEVIATION","STATUS"].map(h=>(
|
||
<span key={h} style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",
|
||
letterSpacing:1.5,paddingBottom:6,borderBottom:`1px solid ${C.border}`}}>{h}</span>
|
||
))}
|
||
</div>
|
||
{/* Base */}
|
||
<div style={{display:"grid",gridTemplateColumns:"55px 110px 90px 100px 90px 70px",
|
||
gap:0,background:`${C.success}06`,padding:"8px 4px",borderRadius:4,marginBottom:2}}>
|
||
{["BASE",`$67,420`,`$${cfg.dcaBase}`,`$${cfg.dcaBase}`,"—",
|
||
<Badge key="s" text="FILLED" color={C.success} small/>].map((v,i)=>(
|
||
<span key={i} style={{color:i===5?undefined:C.text,fontSize:10,
|
||
fontFamily:"JetBrains Mono",display:"flex",alignItems:"center"}}>{v}</span>
|
||
))}
|
||
</div>
|
||
{safeties.map((so)=>(
|
||
<div key={so.lv} style={{display:"grid",gridTemplateColumns:"55px 110px 90px 100px 90px 70px",
|
||
gap:0,padding:"8px 4px",borderRadius:4,marginBottom:2,
|
||
background:so.filled?`${C.success}06`:"transparent"}}>
|
||
{[`SO${so.lv}`,`$${so.price.toLocaleString()}`,`$${so.size}`,
|
||
`$${cfg.dcaBase+safeties.slice(0,so.lv).reduce((a,b)=>a+b.size,0)}`,
|
||
`-${(so.lv*cfg.dcaDev).toFixed(1)}%`,
|
||
<Badge key="s" text={so.filled?"FILLED":"PENDING"}
|
||
color={so.filled?C.success:C.muted} small/>
|
||
].map((v,i)=>(
|
||
<span key={i} style={{color:i===5?undefined:so.filled?C.text:C.muted,fontSize:10,
|
||
fontFamily:"JetBrains Mono",display:"flex",alignItems:"center"}}>{v}</span>
|
||
))}
|
||
</div>
|
||
))}
|
||
{/* TP line */}
|
||
<div style={{marginTop:16,padding:"10px 12px",background:`${C.success}0a`,
|
||
border:`1px solid ${C.success}33`,borderRadius:6,
|
||
display:"flex",justifyContent:"space-between",alignItems:"center"}}>
|
||
<span style={{color:C.success,fontFamily:"Rajdhani",fontSize:11,letterSpacing:1}}>
|
||
TAKE PROFIT TARGET
|
||
</span>
|
||
<span style={{color:C.success,fontFamily:"JetBrains Mono",fontSize:12,fontWeight:600}}>
|
||
$67,734 (+{cfg.dcaTp}% from avg)
|
||
</span>
|
||
</div>
|
||
</Panel>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Tab: Pump Scanner ──────────────────────────────────────────────────────
|
||
function TabPump(){
|
||
return(
|
||
<div style={{display:"grid",gridTemplateColumns:"260px 1fr",gap:14}}>
|
||
<Panel accentColor={C.success}>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16}}>
|
||
<SectionTitle>PUMP SCANNER</SectionTitle>
|
||
<Toggle on={pumpOn} toggle={()=>setPumpOn(!pumpOn)} color={C.success}/>
|
||
</div>
|
||
<Row l="Volume Threshold" v={`${cfg.pumpVol}x avg`}/>
|
||
<Row l="Price Spike" v={`>${cfg.pumpPrice}%`}/>
|
||
<Row l="Leverage" v={`${cfg.pumpLev}x`}/>
|
||
<Row l="Risk/Trade" v={`${cfg.pumpRisk}% equity`}/>
|
||
<Row l="Scan Interval" v="3s"/>
|
||
<Row l="Scanning" v={`${COINS.length} pairs`}/>
|
||
<Row l="Announcements" v="Monitoring"/>
|
||
<div style={{borderTop:`1px solid ${C.border}`,marginTop:10,paddingTop:10}}>
|
||
<div style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",letterSpacing:2,marginBottom:8}}>TODAY STATS</div>
|
||
<Row l="Pumps Detected" v="7" color={C.warn}/>
|
||
<Row l="Trades Entered" v="3" color={C.success}/>
|
||
<Row l="Scanner P&L" v="+$89.20" color={C.success}/>
|
||
</div>
|
||
<div style={{marginTop:12}}>
|
||
<button onClick={()=>setPumpAlerts([])}
|
||
style={{width:"100%",background:`${C.muted}22`,border:`1px solid ${C.border}`,
|
||
color:C.muted,padding:"7px 0",borderRadius:5,fontSize:10}}>
|
||
CLEAR ALERTS
|
||
</button>
|
||
</div>
|
||
</Panel>
|
||
|
||
<Panel>
|
||
<SectionTitle>LIVE ALERTS · {pumpAlerts.length} ACTIVE</SectionTitle>
|
||
{pumpAlerts.length===0
|
||
?<div style={{color:C.muted,textAlign:"center",padding:50,
|
||
fontFamily:"JetBrains Mono",fontSize:12}}>Market calm — no alerts</div>
|
||
:pumpAlerts.map((a,i)=>(
|
||
<div key={i} className={a.act?(a.type==="PUMP"?"pump":"dump"):""}
|
||
style={{padding:18,marginBottom:10,borderRadius:8,
|
||
border:`1px solid ${a.act?(a.type==="PUMP"?C.warn:C.danger):C.border}44`}}>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"flex-start",marginBottom:10}}>
|
||
<div style={{display:"flex",gap:10,alignItems:"center"}}>
|
||
<span style={{color:C.text,fontFamily:"Orbitron",fontSize:20,fontWeight:700}}>{a.coin}</span>
|
||
<Badge text={a.type} color={a.type==="PUMP"?C.warn:C.danger}/>
|
||
{a.act&&<div className="live-dot" style={{width:6,height:6,borderRadius:"50%",
|
||
background:a.type==="PUMP"?C.warn:C.danger}}/>}
|
||
</div>
|
||
<span style={{color:a.type==="PUMP"?C.warn:C.danger,
|
||
fontFamily:"JetBrains Mono",fontSize:28,fontWeight:700}}>{a.pct}</span>
|
||
</div>
|
||
<div style={{display:"grid",gridTemplateColumns:"repeat(3,1fr)",gap:10}}>
|
||
{[["VOLUME MULT",a.vol,C.text],
|
||
["TIME",a.time,C.muted],
|
||
["STATUS",a.act?"ACTIVE":"EXPIRED",a.act?C.success:C.muted]
|
||
].map(([l,v,c])=>(
|
||
<div key={l}>
|
||
<div style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",
|
||
letterSpacing:1.5,marginBottom:3}}>{l}</div>
|
||
<div style={{color:c,fontFamily:"JetBrains Mono",fontSize:14}}>{v}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</Panel>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Tab: Risk Manager ──────────────────────────────────────────────────────
|
||
function TabRisk(){
|
||
const used=Math.max(0,Math.abs(Math.min(0,dailyPnL))/balance*100);
|
||
const perfData=[
|
||
{n:"Mon",pnl:145},{n:"Tue",pnl:-42},{n:"Wed",pnl:234},{n:"Thu",pnl:89},
|
||
{n:"Fri",pnl:-28},{n:"Sat",pnl:312},{n:"Sun",pnl:178}
|
||
];
|
||
return(
|
||
<div style={{display:"flex",flexDirection:"column",gap:14}}>
|
||
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gap:12}}>
|
||
{/* Circuit Breaker */}
|
||
<Panel>
|
||
<SectionTitle>CIRCUIT BREAKER</SectionTitle>
|
||
<div style={{display:"flex",gap:6,marginBottom:12}}>
|
||
{[...Array(cfg.cbLosses)].map((_,i)=>(
|
||
<div key={i} style={{flex:1,height:44,borderRadius:6,
|
||
background:i<cbLosses?`${C.danger}88`:C.dim,
|
||
border:`1px solid ${i<cbLosses?C.danger:C.border}`,
|
||
display:"flex",alignItems:"center",justifyContent:"center",transition:"all .3s"}}>
|
||
<span style={{fontSize:18,color:i<cbLosses?C.danger:C.muted}}>
|
||
{i<cbLosses?"✗":"○"}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<div style={{color:cbLosses>=cfg.cbLosses?C.danger:C.muted,
|
||
fontSize:10,fontFamily:"Rajdhani",marginBottom:10}}>
|
||
{cbLosses>=cfg.cbLosses?"⚠ ACTIVE — Trading paused for 1h":`${cfg.cbLosses-cbLosses} loss(es) until pause`}
|
||
</div>
|
||
<Row l="Consecutive Losses" v={`${cbLosses}/${cfg.cbLosses}`} color={cbLosses>0?C.warn:C.text}/>
|
||
<Row l="Cooldown Period" v="3600s (1h)"/>
|
||
<Row l="Status" v={cbLosses>=cfg.cbLosses?"TRIGGERED":"MONITORING"}
|
||
color={cbLosses>=cfg.cbLosses?C.danger:C.success}/>
|
||
</Panel>
|
||
|
||
{/* Daily Loss */}
|
||
<Panel>
|
||
<SectionTitle>DAILY LOSS LIMIT</SectionTitle>
|
||
<div style={{position:"relative",height:22,background:C.dim,
|
||
borderRadius:11,overflow:"hidden",marginBottom:12}}>
|
||
<div style={{position:"absolute",inset:0,
|
||
width:`${Math.min(used/cfg.maxLoss*100,100)}%`,
|
||
background:used/cfg.maxLoss>0.8?C.danger:used/cfg.maxLoss>0.5?C.warn:C.success,
|
||
borderRadius:11,transition:"width .5s"}}/>
|
||
<div style={{position:"absolute",inset:0,display:"flex",
|
||
alignItems:"center",justifyContent:"center"}}>
|
||
<span style={{color:"white",fontSize:10,fontFamily:"JetBrains Mono",fontWeight:600}}>
|
||
{used.toFixed(2)}% / {cfg.maxLoss}%
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<Row l="Used Today" v={`$${Math.abs(Math.min(0,dailyPnL)).toFixed(2)}`} color={C.danger}/>
|
||
<Row l="Max Allowed" v={`${cfg.maxLoss}% = $${(balance*cfg.maxLoss/100).toFixed(0)}`}/>
|
||
<Row l="Remaining" v={`$${Math.max(0,balance*cfg.maxLoss/100-Math.abs(Math.min(0,dailyPnL))).toFixed(2)}`} color={C.success}/>
|
||
<Row l="Reset" v="00:00 UTC"/>
|
||
</Panel>
|
||
|
||
{/* Performance */}
|
||
<Panel>
|
||
<SectionTitle>PERFORMANCE</SectionTitle>
|
||
<Row l="Total Trades" v={totalTrades}/>
|
||
<Row l="Win Rate" v={`${winRate.toFixed(1)}%`} color={winRate>60?C.success:C.warn}/>
|
||
<Row l="AI Accuracy" v={`${accuracy.toFixed(1)}%`} color={C.blue}/>
|
||
<Row l="Avg Win" v="+$23.4" color={C.success}/>
|
||
<Row l="Avg Loss" v="-$14.2" color={C.danger}/>
|
||
<Row l="Profit Factor" v="1.65" color={C.success}/>
|
||
<Row l="Sharpe Ratio" v="2.14" color={C.success}/>
|
||
<Row l="Max Drawdown" v="-8.3%" color={C.danger}/>
|
||
</Panel>
|
||
</div>
|
||
|
||
{/* Risk params + Weekly chart */}
|
||
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12}}>
|
||
<Panel>
|
||
<SectionTitle>RISK PARAMETERS</SectionTitle>
|
||
<Row l="Risk per Trade" v={`${cfg.risk}% of equity`}/>
|
||
<Row l="Stop Loss" v={`${cfg.sl}%`} color={C.danger}/>
|
||
<Row l="Take Profit" v={`${cfg.tp}%`} color={C.success}/>
|
||
<Row l="R:R Ratio" v={`1:${(cfg.tp/cfg.sl).toFixed(2)}`} color={C.blue}/>
|
||
<Row l="Leverage" v={`${cfg.leverage}x`} color={C.warn}/>
|
||
<Row l="Max Positions" v={cfg.maxPos}/>
|
||
<Row l="Max Margin Usage" v="20%"/>
|
||
<Row l="Max Hold Duration" v={`${cfg.maxHold} candles (${cfg.maxHold}h)`}/>
|
||
<Row l="Compound Mode" v={cfg.compound?"ON":"OFF"} color={cfg.compound?C.success:C.muted}/>
|
||
<Row l="Capital Cap" v="$1,000,000"/>
|
||
</Panel>
|
||
|
||
<Panel>
|
||
<SectionTitle>DAILY P&L · 7 DAYS</SectionTitle>
|
||
<ResponsiveContainer width="100%" height={200}>
|
||
<BarChart data={perfData} margin={{top:4,right:0,left:-20,bottom:0}}>
|
||
<XAxis dataKey="n" tick={{fill:C.muted,fontSize:9,fontFamily:"JetBrains Mono"}}
|
||
tickLine={false} axisLine={false}/>
|
||
<YAxis tick={{fill:C.muted,fontSize:9,fontFamily:"JetBrains Mono"}}
|
||
tickLine={false} axisLine={false}/>
|
||
<Tooltip contentStyle={{background:C.surface,border:`1px solid ${C.border}`,
|
||
borderRadius:6,fontFamily:"JetBrains Mono",fontSize:10,color:C.text}}/>
|
||
<ReferenceLine y={0} stroke={C.border} strokeOpacity={.6}/>
|
||
<Bar dataKey="pnl" fill={C.accent}
|
||
radius={[3,3,0,0]}
|
||
label={false}/>
|
||
</BarChart>
|
||
</ResponsiveContainer>
|
||
</Panel>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Tab: AI Model ──────────────────────────────────────────────────────────
|
||
function TabModel(){
|
||
const features=[
|
||
{n:"rsi_14",v:.124},{n:"funding_rate",v:.098},{n:"volume_ma_ratio",v:.087},
|
||
{n:"oi_change_pct",v:.076},{n:"price_momentum_7",v:.065},{n:"cvd_20",v:.058},
|
||
{n:"atr_14",v:.051},{n:"btc_correlation_20",v:.043},{n:"fear_greed_index",v:.039},
|
||
{n:"order_flow_ratio",v:.033},{n:"obi_proxy",v:.029},{n:"price_skewness_24",v:.025},
|
||
];
|
||
const maxV=features[0].v;
|
||
return(
|
||
<div style={{display:"flex",flexDirection:"column",gap:14}}>
|
||
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:14}}>
|
||
<Panel>
|
||
<SectionTitle>ENSEMBLE MODEL</SectionTitle>
|
||
<Row l="Type" v="XGBoost + LightGBM + RF"/>
|
||
<Row l="Accuracy" v={`${accuracy.toFixed(1)}%`} color={C.blue}/>
|
||
<Row l="Walk-forward" v="4-window validated"/>
|
||
<Row l="Features" v="62 engineered"/>
|
||
<Row l="Training Data" v="12 months · 27 coins"/>
|
||
<Row l="HPO Trials" v="200+ (Optuna)"/>
|
||
<Row l="Min Confidence" v={`${cfg.minConf}%`}/>
|
||
<Row l="Last Retrain" v="4h ago"/>
|
||
<Row l="Next Auto-Retrain" v="20h"/>
|
||
<Row l="Min Retrain Acc." v={`${cfg.retrainMinAcc}%`}/>
|
||
<div style={{borderTop:`1px solid ${C.border}`,marginTop:12,paddingTop:12}}>
|
||
<div style={{display:"flex",gap:8,marginBottom:8}}>
|
||
<Toggle on={retrainOn} toggle={()=>setRetrainOn(!retrainOn)}
|
||
label="Auto-Retrain" color={C.purple}/>
|
||
</div>
|
||
</div>
|
||
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:8,marginTop:8}}>
|
||
<button style={{background:`${C.blue}15`,border:`1px solid ${C.blue}44`,
|
||
color:C.blue,padding:"8px 0",borderRadius:5,fontSize:11}}>
|
||
RETRAIN NOW
|
||
</button>
|
||
<button style={{background:`${C.accent}15`,border:`1px solid ${C.accent}44`,
|
||
color:C.accent,padding:"8px 0",borderRadius:5,fontSize:11}}>
|
||
BACKTEST
|
||
</button>
|
||
<button style={{background:`${C.warn}15`,border:`1px solid ${C.warn}44`,
|
||
color:C.warn,padding:"8px 0",borderRadius:5,fontSize:11}}>
|
||
DOWNLOAD DATA
|
||
</button>
|
||
<button style={{background:`${C.muted}22`,border:`1px solid ${C.border}`,
|
||
color:C.muted,padding:"8px 0",borderRadius:5,fontSize:11}}>
|
||
VIEW LOGS
|
||
</button>
|
||
</div>
|
||
</Panel>
|
||
|
||
<Panel>
|
||
<SectionTitle>FEATURE IMPORTANCE · TOP 12</SectionTitle>
|
||
{features.map(({n,v},i)=>(
|
||
<div key={n} style={{marginBottom:8}}>
|
||
<div style={{display:"flex",justifyContent:"space-between",marginBottom:2}}>
|
||
<span style={{color:C.muted,fontSize:9,fontFamily:"JetBrains Mono"}}>{n}</span>
|
||
<span style={{color:C.text,fontSize:9,fontFamily:"JetBrains Mono"}}>{(v*100).toFixed(1)}%</span>
|
||
</div>
|
||
<div style={{height:4,background:C.dim,borderRadius:2}}>
|
||
<div style={{width:`${v/maxV*100}%`,height:"100%",borderRadius:2,
|
||
background:i===0?C.accent:i<3?C.blue:C.muted,
|
||
opacity:Math.max(0.3,1-i*.06)}}/>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</Panel>
|
||
</div>
|
||
|
||
<Panel>
|
||
<SectionTitle>TRADING UNIVERSE · {COINS.length} COINS</SectionTitle>
|
||
<div style={{display:"flex",flexWrap:"wrap",gap:7}}>
|
||
{COINS.map(coin=>{
|
||
const hasPos=positions.some(p=>p.coin===coin);
|
||
return(
|
||
<div key={coin} style={{
|
||
background:hasPos?`${C.accent}18`:C.dim,
|
||
border:`1px solid ${hasPos?C.accent:C.border}`,
|
||
borderRadius:5,padding:"4px 11px",
|
||
...(hasPos?{boxShadow:`0 0 8px ${C.accent}33`}:{})}}>
|
||
<span style={{color:hasPos?C.accent:C.muted,
|
||
fontFamily:"JetBrains Mono",fontSize:11}}>{coin}</span>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</Panel>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Tab: Config ────────────────────────────────────────────────────────────
|
||
function TabConfig(){
|
||
function CfgSlider({label,field,min,max,step=1,suffix=""}){
|
||
return(
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",
|
||
marginBottom:11,paddingBottom:10,borderBottom:`1px solid ${C.border}22`}}>
|
||
<span style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani",flex:1}}>{label}</span>
|
||
<div style={{display:"flex",alignItems:"center",gap:8}}>
|
||
<input type="range" min={min} max={max} step={step} value={cfg[field]}
|
||
onChange={e=>setC(field,parseFloat(e.target.value))} style={{width:90}}/>
|
||
<span style={{color:C.accent,fontFamily:"JetBrains Mono",fontSize:11,
|
||
minWidth:46,textAlign:"right"}}>{cfg[field]}{suffix}</span>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
return(
|
||
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gap:14}}>
|
||
{/* Bot Config */}
|
||
<div style={{display:"flex",flexDirection:"column",gap:12}}>
|
||
<Panel>
|
||
<SectionTitle>EXCHANGE & MODE</SectionTitle>
|
||
<div style={{marginBottom:14}}>
|
||
<div style={{color:C.muted,fontSize:8,fontFamily:"Rajdhani",
|
||
letterSpacing:2,marginBottom:8}}>EXCHANGE</div>
|
||
<div style={{display:"flex",flexWrap:"wrap",gap:5}}>
|
||
{EXCHANGES.map(ex=>(
|
||
<button key={ex} onClick={()=>setExchange(ex)}
|
||
style={{background:exchange===ex?`${C.accent}1a`:C.dim,
|
||
border:`1px solid ${exchange===ex?C.accent:C.border}`,
|
||
color:exchange===ex?C.accent:C.muted,
|
||
padding:"4px 10px",borderRadius:4,fontSize:10}}>
|
||
{ex}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
<div style={{marginBottom:14}}>
|
||
<Toggle on={paperMode} toggle={()=>setPaperMode(!paperMode)}
|
||
label={paperMode?"📄 Paper Mode (Simulation)":"💰 Live Trading"} color={paperMode?C.blue:C.danger}/>
|
||
</div>
|
||
<div style={{marginBottom:6}}>
|
||
<Toggle on={cfg.compound} toggle={()=>setC("compound",!cfg.compound)}
|
||
label="Compound Mode" color={C.accent}/>
|
||
</div>
|
||
</Panel>
|
||
|
||
<Panel>
|
||
<SectionTitle>BOT PARAMETERS</SectionTitle>
|
||
<CfgSlider label="Leverage" field="leverage" min={1} max={20} suffix="x"/>
|
||
<CfgSlider label="Risk per Trade" field="risk" min={0.5} max={10} step={0.5} suffix="%"/>
|
||
<CfgSlider label="Stop Loss" field="sl" min={0.5} max={5} step={0.1} suffix="%"/>
|
||
<CfgSlider label="Take Profit" field="tp" min={0.5} max={10} step={0.1} suffix="%"/>
|
||
<CfgSlider label="Max Positions" field="maxPos" min={1} max={10}/>
|
||
<CfgSlider label="Max Daily Loss" field="maxLoss" min={1} max={20} suffix="%"/>
|
||
<CfgSlider label="Min Confidence" field="minConf" min={60} max={95} suffix="%"/>
|
||
<CfgSlider label="Max Hold (candles)" field="maxHold" min={1} max={24}/>
|
||
</Panel>
|
||
</div>
|
||
|
||
{/* Grid + DCA */}
|
||
<div style={{display:"flex",flexDirection:"column",gap:12}}>
|
||
<Panel accentColor={C.blue}>
|
||
<SectionTitle>GRID BOT</SectionTitle>
|
||
<div style={{marginBottom:12}}>
|
||
<Toggle on={gridOn} toggle={()=>setGridOn(!gridOn)} label="Grid Bot" color={C.blue}/>
|
||
</div>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",
|
||
marginBottom:11,paddingBottom:10,borderBottom:`1px solid ${C.border}22`}}>
|
||
<span style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani"}}>Coin</span>
|
||
<select value={cfg.gridCoin} onChange={e=>setC("gridCoin",e.target.value)}>
|
||
{["BTC","ETH","SOL","BNB"].map(c=><option key={c}>{c}</option>)}
|
||
</select>
|
||
</div>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",
|
||
marginBottom:11,paddingBottom:10,borderBottom:`1px solid ${C.border}22`}}>
|
||
<span style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani"}}>Strategy</span>
|
||
<select value={cfg.gridStrategy} onChange={e=>setC("gridStrategy",e.target.value)}>
|
||
{GRID_STRATEGIES.map(s=><option key={s}>{s}</option>)}
|
||
</select>
|
||
</div>
|
||
<CfgSlider label="Grid Levels" field="gridLevels" min={5} max={50}/>
|
||
<CfgSlider label="Total USDT" field="gridUsdt" min={50} max={5000} step={50} suffix=" $"/>
|
||
</Panel>
|
||
|
||
<Panel accentColor={C.warn}>
|
||
<SectionTitle>DCA BOT</SectionTitle>
|
||
<div style={{marginBottom:12}}>
|
||
<Toggle on={dcaOn} toggle={()=>setDcaOn(!dcaOn)} label="DCA Bot" color={C.warn}/>
|
||
</div>
|
||
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",
|
||
marginBottom:11,paddingBottom:10,borderBottom:`1px solid ${C.border}22`}}>
|
||
<span style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani"}}>Coin</span>
|
||
<select value={cfg.dcaCoin} onChange={e=>setC("dcaCoin",e.target.value)}>
|
||
{["BTC","ETH","SOL","BNB"].map(c=><option key={c}>{c}</option>)}
|
||
</select>
|
||
</div>
|
||
<CfgSlider label="Base Order" field="dcaBase" min={10} max={1000} step={10} suffix=" $"/>
|
||
<CfgSlider label="Safety Order" field="dcaSafety" min={10} max={1000} step={10} suffix=" $"/>
|
||
<CfgSlider label="Max Safety Orders" field="dcaMaxSafety" min={1} max={10}/>
|
||
<CfgSlider label="Price Deviation" field="dcaDev" min={0.5} max={5} step={0.1} suffix="%"/>
|
||
<CfgSlider label="Take Profit" field="dcaTp" min={0.5} max={10} step={0.1} suffix="%"/>
|
||
</Panel>
|
||
</div>
|
||
|
||
{/* Pump + Model */}
|
||
<div style={{display:"flex",flexDirection:"column",gap:12}}>
|
||
<Panel accentColor={C.success}>
|
||
<SectionTitle>PUMP SCANNER</SectionTitle>
|
||
<div style={{marginBottom:12}}>
|
||
<Toggle on={pumpOn} toggle={()=>setPumpOn(!pumpOn)} label="Pump Scanner" color={C.success}/>
|
||
</div>
|
||
<CfgSlider label="Volume Multiplier" field="pumpVol" min={2} max={20} step={0.5} suffix="x"/>
|
||
<CfgSlider label="Price Spike" field="pumpPrice" min={1} max={15} suffix="%"/>
|
||
<CfgSlider label="Leverage" field="pumpLev" min={1} max={20} suffix="x"/>
|
||
<CfgSlider label="Risk per Trade" field="pumpRisk" min={1} max={20} suffix="%"/>
|
||
</Panel>
|
||
|
||
<Panel accentColor={C.purple}>
|
||
<SectionTitle>AI MODEL</SectionTitle>
|
||
<div style={{marginBottom:12}}>
|
||
<Toggle on={retrainOn} toggle={()=>setRetrainOn(!retrainOn)}
|
||
label="Auto-Retrain" color={C.purple}/>
|
||
</div>
|
||
<div style={{marginBottom:12}}>
|
||
<Toggle on={regimeFilter} toggle={()=>setRegimeFilter(!regimeFilter)}
|
||
label="Regime Filter (HMM)" color={C.purple}/>
|
||
</div>
|
||
<CfgSlider label="Retrain Interval" field="retrainInterval" min={1} max={168} suffix="h"/>
|
||
<CfgSlider label="Min Retrain Acc." field="retrainMinAcc" min={50} max={90} suffix="%"/>
|
||
</Panel>
|
||
|
||
<Panel>
|
||
<SectionTitle>CIRCUIT BREAKER</SectionTitle>
|
||
<CfgSlider label="Max Consecutive Losses" field="cbLosses" min={1} max={10}/>
|
||
<div style={{marginTop:6}}>
|
||
<Row l="Cooldown" v="3600s (1h)"/>
|
||
<Row l="Status" v={cbLosses>=cfg.cbLosses?"TRIGGERED":"MONITORING"}
|
||
color={cbLosses>=cfg.cbLosses?C.danger:C.success}/>
|
||
</div>
|
||
</Panel>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Nav items ──────────────────────────────────────────────────────────────
|
||
const nav=[
|
||
{id:"overview",label:"OVERVIEW",dot:null},
|
||
{id:"aibot",label:"AI BOT",dot:botOn?C.accent:null},
|
||
{id:"grid",label:"GRID BOT",dot:gridOn?C.blue:null},
|
||
{id:"dca",label:"DCA BOT",dot:dcaOn?C.warn:null},
|
||
{id:"pump",label:"PUMP SCAN",dot:pumpOn?C.success:null},
|
||
{id:"risk",label:"RISK MGR",dot:cbLosses>0?C.warn:null},
|
||
{id:"model",label:"AI MODEL",dot:retrainOn?C.purple:null},
|
||
{id:"config",label:"CONFIG",dot:null},
|
||
];
|
||
|
||
// ── Final render ───────────────────────────────────────────────────────────
|
||
return(
|
||
<div style={{background:C.bg,minHeight:"100vh",color:C.text,fontFamily:"Rajdhani, sans-serif"}}>
|
||
<style>{CSS}</style>
|
||
|
||
{/* ── Top Bar ─────────────────────────────────────────────────────── */}
|
||
<div style={{background:C.surface,borderBottom:`1px solid ${C.border}`,
|
||
padding:"0 24px",height:52,display:"flex",alignItems:"center",
|
||
justifyContent:"space-between",position:"sticky",top:0,zIndex:100}}>
|
||
{/* Logo */}
|
||
<div style={{display:"flex",alignItems:"center",gap:12}}>
|
||
<span style={{fontFamily:"Orbitron",fontSize:17,fontWeight:900,
|
||
color:C.accent,letterSpacing:2,textShadow:`0 0 20px ${C.accent}66`}}>
|
||
DEEP<span style={{color:C.text}}>ALPHA</span>
|
||
</span>
|
||
<div style={{background:`${C.accent}18`,border:`1px solid ${C.accent}44`,
|
||
borderRadius:3,padding:"1px 9px"}}>
|
||
<span style={{color:C.accent,fontSize:8,fontFamily:"JetBrains Mono",letterSpacing:2}}>
|
||
PRO V11
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Center stats */}
|
||
<div style={{display:"flex",gap:24,alignItems:"center"}}>
|
||
<div style={{textAlign:"center"}}>
|
||
<div style={{color:C.muted,fontSize:7,fontFamily:"Rajdhani",letterSpacing:2}}>BALANCE</div>
|
||
<div style={{color:C.accent,fontFamily:"JetBrains Mono",fontSize:14,fontWeight:600}}>
|
||
${balance.toLocaleString("en-US",{minimumFractionDigits:2})}
|
||
</div>
|
||
</div>
|
||
<div style={{textAlign:"center"}}>
|
||
<div style={{color:C.muted,fontSize:7,fontFamily:"Rajdhani",letterSpacing:2}}>DAILY P&L</div>
|
||
<div style={{color:dailyColor,fontFamily:"JetBrains Mono",fontSize:14,fontWeight:600}}>
|
||
{dailyPnL>=0?"+":""}${fmt2(dailyPnL)}
|
||
</div>
|
||
</div>
|
||
<div style={{textAlign:"center"}}>
|
||
<div style={{color:C.muted,fontSize:7,fontFamily:"Rajdhani",letterSpacing:2}}>REGIME</div>
|
||
<div style={{color:regimeColor,fontFamily:"Orbitron",fontSize:12,fontWeight:700}}>
|
||
{regimeIcon} {regime}
|
||
</div>
|
||
</div>
|
||
<div style={{textAlign:"center"}}>
|
||
<div style={{color:C.muted,fontSize:7,fontFamily:"Rajdhani",letterSpacing:2}}>POSITIONS</div>
|
||
<div style={{color:C.text,fontFamily:"JetBrains Mono",fontSize:14}}>
|
||
{positions.length}<span style={{color:C.muted}}>/{cfg.maxPos}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Right status */}
|
||
<div style={{display:"flex",alignItems:"center",gap:16}}>
|
||
<div style={{background:paperMode?`${C.blue}18`:`${C.danger}18`,
|
||
border:`1px solid ${paperMode?C.blue:C.danger}44`,
|
||
borderRadius:4,padding:"3px 10px"}}>
|
||
<span style={{color:paperMode?C.blue:C.danger,fontSize:10,fontFamily:"Rajdhani",letterSpacing:1}}>
|
||
{paperMode?"📄 PAPER":"💰 LIVE"}
|
||
</span>
|
||
</div>
|
||
<div style={{fontSize:10,fontFamily:"Rajdhani",color:C.muted}}>
|
||
{exchange.toUpperCase()}
|
||
</div>
|
||
<div style={{display:"flex",alignItems:"center",gap:6}}>
|
||
<div className={botOn?"live-dot":""} style={{width:7,height:7,borderRadius:"50%",
|
||
background:botOn?C.success:C.danger,
|
||
...(botOn?{boxShadow:`0 0 8px ${C.success}`}:{})}}/>
|
||
<span style={{color:botOn?C.success:C.danger,fontSize:10,fontFamily:"JetBrains Mono"}}>
|
||
{botOn?"ACTIVE":"STOPPED"}
|
||
</span>
|
||
</div>
|
||
<span style={{color:C.muted,fontFamily:"JetBrains Mono",fontSize:10}}>
|
||
{clock.toLocaleTimeString("fr-FR")}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ── Body ────────────────────────────────────────────────────────── */}
|
||
<div style={{display:"flex"}}>
|
||
{/* Sidebar */}
|
||
<div style={{width:168,background:C.surface,borderRight:`1px solid ${C.border}`,
|
||
minHeight:"calc(100vh - 52px)",padding:"12px 0",flexShrink:0,position:"sticky",
|
||
top:52,height:"calc(100vh - 52px)",overflowY:"auto"}}>
|
||
{nav.map(item=>(
|
||
<div key={item.id} className="nav-btn" onClick={()=>setTab(item.id)}
|
||
style={{display:"flex",alignItems:"center",gap:10,padding:"10px 16px",
|
||
cursor:"pointer",margin:"1px 8px",borderRadius:6,
|
||
background:tab===item.id?`${C.accent}12`:"transparent",
|
||
borderLeft:tab===item.id?`2px solid ${C.accent}`:"2px solid transparent",
|
||
transition:"all .2s",userSelect:"none"}}>
|
||
{item.dot&&<div style={{width:5,height:5,borderRadius:"50%",
|
||
background:item.dot,boxShadow:`0 0 5px ${item.dot}`,flexShrink:0}}/>}
|
||
{!item.dot&&<div style={{width:5,height:5,borderRadius:"50%",
|
||
background:"transparent",flexShrink:0}}/>}
|
||
<span style={{color:tab===item.id?C.accent:C.muted,
|
||
fontSize:11,fontFamily:"Rajdhani",letterSpacing:1.5,fontWeight:tab===item.id?600:400}}>
|
||
{item.label}
|
||
</span>
|
||
</div>
|
||
))}
|
||
|
||
{/* Quick toggles */}
|
||
<div style={{borderTop:`1px solid ${C.border}`,margin:"14px 10px 0",paddingTop:14}}>
|
||
<div style={{color:C.dim,fontSize:7,letterSpacing:2.5,fontFamily:"Rajdhani",
|
||
marginBottom:10,paddingLeft:6}}>QUICK CONTROLS</div>
|
||
{[
|
||
{l:"AI Bot",on:botOn,f:()=>setBotOn(!botOn),c:C.accent},
|
||
{l:"Grid",on:gridOn,f:()=>setGridOn(!gridOn),c:C.blue},
|
||
{l:"DCA",on:dcaOn,f:()=>setDcaOn(!dcaOn),c:C.warn},
|
||
{l:"Pump",on:pumpOn,f:()=>setPumpOn(!pumpOn),c:C.success},
|
||
].map(({l,on,f,c})=>(
|
||
<div key={l} onClick={f} style={{display:"flex",justifyContent:"space-between",
|
||
alignItems:"center",padding:"6px 6px",cursor:"pointer",borderRadius:4,
|
||
transition:"background .15s"}}>
|
||
<span style={{color:C.muted,fontSize:10,fontFamily:"Rajdhani"}}>{l}</span>
|
||
<div style={{width:30,height:16,borderRadius:8,
|
||
background:on?c:C.dim,position:"relative",transition:"background .3s"}}>
|
||
<div style={{position:"absolute",top:2,left:on?14:2,width:12,height:12,
|
||
borderRadius:6,background:"white",transition:"left .25s"}}/>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* System info */}
|
||
<div style={{borderTop:`1px solid ${C.border}`,margin:"14px 10px 0",paddingTop:14}}>
|
||
<div style={{color:C.dim,fontSize:7,letterSpacing:2.5,fontFamily:"Rajdhani",
|
||
marginBottom:8,paddingLeft:6}}>SYSTEM</div>
|
||
<div style={{padding:"0 6px"}}>
|
||
{[["CPU","12%"],["RAM","2.1GB"],["Uptime","4h 23m"],["Loop","60s"]].map(([l,v])=>(
|
||
<div key={l} style={{display:"flex",justifyContent:"space-between",marginBottom:5}}>
|
||
<span style={{color:C.muted,fontSize:9,fontFamily:"Rajdhani"}}>{l}</span>
|
||
<span style={{color:C.text,fontSize:9,fontFamily:"JetBrains Mono"}}>{v}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Main content */}
|
||
<div style={{flex:1,padding:18,overflowY:"auto",maxHeight:"calc(100vh - 52px)"}}>
|
||
{tab==="overview" && <TabOverview/>}
|
||
{tab==="aibot" && <TabAIBot/>}
|
||
{tab==="grid" && <TabGrid/>}
|
||
{tab==="dca" && <TabDCA/>}
|
||
{tab==="pump" && <TabPump/>}
|
||
{tab==="risk" && <TabRisk/>}
|
||
{tab==="model" && <TabModel/>}
|
||
{tab==="config" && <TabConfig/>}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|