Spaces:
No application file
No application file
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1.0"> | |
| <title>AlphaForge Pro</title> | |
| <script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script> | |
| <style> | |
| :root{--bg:#0a0e17;--card:#111827;--accent:#38bdf8;--green:#10b981;--red:#ef4444;--yellow:#f59e0b;--text:#94a3b8;--bright:#e2e8f0;--border:#1e293b} | |
| *{margin:0;padding:0;box-sizing:border-box} | |
| body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,sans-serif} | |
| .header{background:var(--card);padding:12px 24px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--border)} | |
| .header h1{color:var(--accent);font-size:20px;font-weight:800} | |
| .header .badge{color:var(--text);font-size:10px;padding:2px 8px;background:rgba(30,41,59,0.8);border-radius:4px} | |
| .header .live{color:var(--green);font-size:10px} | |
| .kpi-row{display:flex;gap:10px;flex-wrap:wrap;padding:16px 24px} | |
| .kpi{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:14px 18px;text-align:center;flex:1;min-width:100px} | |
| .kpi-val{font-size:20px;font-weight:700} | |
| .kpi-lbl{color:var(--text);font-size:10px;text-transform:uppercase;margin-top:4px;letter-spacing:.5px} | |
| .main-grid{display:grid;grid-template-columns:280px 1fr;gap:16px;padding:0 24px 40px} | |
| .panel{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:16px;margin-bottom:16px} | |
| .panel-title{color:var(--bright);font-weight:600;font-size:13px;margin-bottom:12px} | |
| .pill{display:inline-block;padding:6px 12px;margin:3px;border:1px solid var(--border);border-radius:8px;font-size:12px} | |
| .pill .sym{color:var(--accent);font-weight:700;margin-right:6px} | |
| .alert-row{padding:8px 12px;margin:4px 0;border-left:3px solid var(--accent);border-radius:4px;font-size:11px} | |
| .alert-row b{font-size:11px} | |
| .tabs{display:flex;gap:4px;margin-bottom:12px} | |
| .tab{padding:6px 16px;border-radius:6px;cursor:pointer;font-size:12px;background:transparent;color:var(--text);border:1px solid transparent} | |
| .tab.active{background:var(--card);color:var(--accent);border-color:var(--border)} | |
| .tab-content{display:none} | |
| .tab-content.active{display:block} | |
| .chart{min-height:300px} | |
| @media(max-width:900px){.main-grid{grid-template-columns:1fr}} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <div style="display:flex;align-items:center;gap:12px"><h1>AlphaForge Pro</h1><span class="badge">v2.0</span></div> | |
| <div style="display:flex;align-items:center;gap:16px"> | |
| <span class="live">β LIVE</span> | |
| <span style="color:var(--text);font-size:12px" id="clock"></span> | |
| </div> | |
| </div> | |
| <div class="kpi-row" id="kpis"></div> | |
| <div class="main-grid"> | |
| <div> | |
| <div class="panel"><div class="panel-title">Positions</div><div id="positions"></div></div> | |
| <div class="panel"><div class="panel-title">Market Regime</div><div class="chart" id="regime"></div></div> | |
| <div class="panel"><div class="panel-title">Sentiment</div><div class="chart" id="sentiment"></div></div> | |
| <div class="panel"><div class="panel-title">Alerts</div><div id="alerts"></div></div> | |
| </div> | |
| <div> | |
| <div class="tabs"> | |
| <div class="tab active" onclick="switchTab('pnl')">PnL & Drawdown</div> | |
| <div class="tab" onclick="switchTab('risk')">Risk Metrics</div> | |
| <div class="tab" onclick="switchTab('weights')">Portfolio Weights</div> | |
| </div> | |
| <div class="tab-content active" id="tab-pnl"><div class="chart" id="chart-pnl"></div></div> | |
| <div class="tab-content" id="tab-risk"><div class="chart" id="chart-risk"></div></div> | |
| <div class="tab-content" id="tab-weights"><div class="chart" id="chart-weights"></div></div> | |
| </div> | |
| </div> | |
| <script> | |
| // βββ Data ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const sharpe=1.82,sortino=2.14,maxDD=-4.3,var95=18340,calmar=4.2; | |
| const positions={SPY:0.18,QQQ:0.15,AAPL:0.10,MSFT:0.12,GOOGL:0.08,AMZN:0.07,META:0.06,NVDA:0.14,TSLA:0.05,JPM:0.05}; | |
| const sentiment={AAPL:0.72,MSFT:0.65,NVDA:0.88,TSLA:-0.34,SPY:0.15}; | |
| const alertList=[{t:"10:23",lv:"info",ti:"Backtest Complete",tx:"Sharpe: 1.82, Max DD: -4.3%"},{t:"10:24",lv:"warn",ti:"Volatility Spike",tx:"VIX at 28.5, reducing exposure"},{t:"10:25",lv:"info",ti:"Regime Change",tx:"Switched to bull - increasing equity"}]; | |
| const ac={"info":"#38bdf8","warn":"#f59e0b","error":"#ef4444"}; | |
| // Generate returns and PnL | |
| let rng=Math.seedrandom?(()=>{let s=42;return ()=>((s=(s*16807)%2147483647)/2147483647)})():Math.random; | |
| let rets=[],pnl=0,pnls=[],dd=0,mx=0,dds=[]; | |
| for(let i=0;i<252;i++){ | |
| let r=0.0008+rng()*0.012-(i>100&&i<140?0.003:0); | |
| if(i>140&&i<180)r=0.0025+rng()*0.012; | |
| rets.push(r);pnl+=r*1e6;pnls.push(pnl); | |
| } | |
| let cum=1;rets.forEach(r=>{cum*=(1+r);mx=Math.max(mx,cum);dds.push(((cum-mx)/mx)*100);}); | |
| // Rolling metrics | |
| let rollingSharpe=[],rollingVol=[]; | |
| for(let i=0;i<rets.length;i++){ | |
| if(i<21){rollingSharpe.push(sharpe);rollingVol.push(20);continue} | |
| let w=rets.slice(i-21,i+1),m=w.reduce((a,b)=>a+b)/w.length; | |
| let s=Math.sqrt(w.reduce((a,b)=>a+b*b,0)/w.length-m*m); | |
| rollingSharpe.push(s>0?m/s*Math.sqrt(252):sharpe); | |
| rollingVol.push(s*Math.sqrt(252)*100); | |
| } | |
| let dates=[];let d=new Date(2024,0,1); | |
| for(let i=0;i<252;i++){dates.push(d.toISOString().slice(0,10));d.setDate(d.getDate()+1);if(d.getDay()===6)d.setDate(d.getDate()+2);if(d.getDay()===0)d.setDate(d.getDate()+1)} | |
| // βββ KPIs ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| document.getElementById("kpis").innerHTML=[ | |
| {v:"$"+pnls[pnls.length-1].toLocaleString("en-US",{maximumFractionDigits:0}),l:"Cumulative PnL",c:"var(--accent)"}, | |
| {v:sharpe.toFixed(2),l:"Sharpe",c:sharpe>=1?"var(--green)":"var(--yellow)"}, | |
| {v:sortino.toFixed(2),l:"Sortino",c:"var(--accent)"}, | |
| {v:maxDD.toFixed(1)+"%",l:"Max Drawdown",c:"var(--red)"}, | |
| {v:"$"+var95.toLocaleString(),l:"VaR 95%",c:"var(--yellow)"}, | |
| {v:calmar.toFixed(2),l:"Calmar",c:"var(--accent)"}, | |
| {v:"+12.1%",l:"Alpha",c:"var(--green)"}, | |
| {v:"0.95",l:"Beta",c:"var(--accent)"} | |
| ].map(k=>'<div class="kpi"><div class="kpi-val" style="color:'+k.c+'">'+k.v+'</div><div class="kpi-lbl">'+k.l+'</div></div>').join(""); | |
| // βββ Positions βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| document.getElementById("positions").innerHTML=Object.entries(positions).sort((a,b)=>b[1]-a[1]).map(([s,w])=>'<span class="pill"><span class="sym">'+s+'</span>'+(w*100).toFixed(1)+'%</span>').join(""); | |
| // βββ Alerts ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| document.getElementById("alerts").innerHTML=alertList.map(a=>'<div class="alert-row" style="border-left-color:'+ac[a.lv]+'"><b style="color:'+ac[a.lv]+'">['+a.t+'] '+a.ti+'</b> '+a.tx+'</div>').join(""); | |
| // βββ Charts ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const lo={template:"plotly_dark",paper_bgcolor:"#0a0e17",plot_bgcolor:"#0a0e17",font:{color:"#94a3b8",size:11},showlegend:false,margin:{l:8,r:24,t:28,b:8}}; | |
| // PnL & Drawdown | |
| Plotly.newPlot("chart-pnl",[ | |
| {type:"scatter",x:dates,y:pnls,line:{color:"#38bdf8",width:2},fill:"tozeroy",fillcolor:"rgba(56,189,248,0.08)",name:"PnL"}, | |
| {type:"scatter",x:dates,y:dds,line:{color:"#ef4444",width:1.5},fill:"tozeroy",fillcolor:"rgba(239,68,68,0.08)",name:"Drawdown %",yaxis:"y2"} | |
| ],{...lo,height:400,yaxis:{tickprefix:"$",tickformat:",.0f",title:"PnL $"},yaxis2:{ticksuffix:"%",title:"Drawdown %",overlaying:"y",side:"right"}}); | |
| // Risk | |
| Plotly.newPlot("chart-risk",[ | |
| {type:"scatter",x:dates,y:rollingSharpe,line:{color:"#38bdf8",width:2},name:"Rolling Sharpe"}, | |
| {type:"scatter",x:dates,y:Array(dates.length).fill(sharpe),line:{color:"rgba(239,68,68,0.5)",dash:"dot",width:1},name:"Average"}, | |
| {type:"scatter",x:dates,y:rollingVol,line:{color:"#f59e0b",width:2},name:"Volatility %",yaxis:"y2"} | |
| ],{...lo,height:400,yaxis:{title:"Sharpe"},yaxis2:{title:"Vol %",ticksuffix:"%",overlaying:"y",side:"right"}}); | |
| // Weights | |
| let wp=Object.entries(positions).sort((a,b)=>b[1]-a[1]); | |
| Plotly.newPlot("chart-weights",[{type:"bar",x:wp.map(e=>e[0]),y:wp.map(e=>e[1]*100),marker:{color:["#38bdf8","#38bdf8","#38bdf8","#6366f1","#6366f1","#6366f1","#10b981","#10b981","#8b5cf6","#8b5cf6"]}}],{...lo,height:340,yaxis:{title:"Weight %",ticksuffix:"%"}}); | |
| // Regime | |
| Plotly.newPlot("regime",[{type:"pie",labels:["Bull","Bear","High Vol","Neutral"],values:[0.45,0.12,0.28,0.15],hole:0.55,marker:{colors:["#10b981","#ef4444","#f59e0b","#64748b"]},textinfo:"label+percent",textfont:{color:"#e2e8f0",size:11}}],{...lo,height:240}); | |
| // Sentiment | |
| Plotly.newPlot("sentiment",[{type:"bar",x:Object.keys(sentiment),y:Object.values(sentiment),marker:{color:Object.values(sentiment).map(v=>v>0?"#10b981":"#ef4444")}}],{...lo,height:240,yaxis:{title:"Score",range:[-1.1,1.1]}}); | |
| // βββ Tabs ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function switchTab(t){ | |
| document.querySelectorAll(".tab").forEach(e=>e.classList.remove("active")); | |
| document.querySelectorAll(".tab-content").forEach(e=>e.classList.remove("active")); | |
| event.target.classList.add("active"); | |
| document.getElementById("tab-"+t).classList.add("active"); | |
| } | |
| // βββ Clock βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| setInterval(()=>{document.getElementById("clock").textContent=new Date().toLocaleString();},1000); | |
| document.getElementById("clock").textContent=new Date().toLocaleString(); | |
| </script> | |
| </body> | |
| </html> | |