// Operational pages: Signals, Trades, Risk, Backtester, Alerts, Logs. const { useState: upS, useEffect: upE, useMemo: upM } = React; // ===================================================================== // SIGNALS PAGE — TradingView webhook log // ===================================================================== function SignalsPage({ accent }) { const [filter, setFilter] = upS("All"); const filtered = SIGNALS.filter((s) => filter === "All" || s.status === filter.toLowerCase().replace(" ", "_")); // Stats const total = SIGNALS.length; const executed = SIGNALS.filter((s) => s.status === "executed").length; const rejected = SIGNALS.filter((s) => s.status === "rejected").length; const dedup = SIGNALS.filter((s) => s.status === "deduplicated").length; const stopped = SIGNALS.filter((s) => s.status === "stopped_out").length; const avgLat = Math.round(SIGNALS.reduce((a, s) => a + s.latencyMs, 0) / SIGNALS.length); return (
{/* KPI strip */}
{/* Funnel + latency */}
s.latencyMs)} h={140} />
milliseconds · target < 2000ms
p50: 461ms
p95: 689ms
p99: 812ms
POST https://api.helix.trade/webhook{"\n"} X-Webhook-Secret: ••••••••••{"\n\n"} {"{\n"} {" "}"signal": "BUY",{"\n"} {" "}"symbol": "BTC/USDT",{"\n"} {" "}"price": {"{{close}}"},{"\n"} {" "}"signal_id": {"{{strategy.order.id}}"}{"\n"} {"}"}
} style={{ flex: 1, justifyContent: "center" }}>Copy URL } style={{ flex: 1, justifyContent: "center" }}>Rotate secret
{/* Signal log table */} {["All", "Executed", "Rejected", "Deduplicated", "Stopped out"].map((f) => ( ))}
} padded={false}> {filtered.map((s, i) => { const statusCfg = { executed: { c: "var(--profit)", lbl: "Executed" }, deduplicated: { c: "var(--text-3)", lbl: "Deduplicated" }, rejected: { c: "var(--loss)", lbl: "Rejected" }, stopped_out: { c: "var(--warn)", lbl: "Stopped out" }, }[s.status]; return ( ); })}
Received Signal ID Side Price Status Reason Latency
{fmtRel(s.t)} {s.id} {s.side} ${s.price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} {statusCfg.lbl} {s.reason || "—"} 700 ? "var(--warn)" : "var(--text-2)" }}>{s.latencyMs}ms
); } function CodeBlock({ children }) { return (
{children}
); } // ===================================================================== // TRADES PAGE // ===================================================================== function TradesPage({ accent, activeExchange }) { const [side, setSide] = upS("All"); const [status, setStatus] = upS("All"); const [exch, setExch] = upS("All"); const filtered = TRADES.filter((t) => (side === "All" || t.side === side) && (status === "All" || t.status === status.toLowerCase().replace(" ", "_")) && (exch === "All" || t.exchange === exch.toLowerCase()) ); const closed = TRADES.filter((t) => t.pnl != null); const wins = closed.filter((t) => t.pnl > 0).length; const losses = closed.filter((t) => t.pnl < 0).length; const totalPnL = closed.reduce((a, t) => a + t.pnl, 0); const avgWin = closed.filter((t) => t.pnl > 0).reduce((a, t) => a + t.pnl, 0) / Math.max(1, wins); const avgLoss = closed.filter((t) => t.pnl < 0).reduce((a, t) => a + t.pnl, 0) / Math.max(1, losses); const profitFactor = Math.abs(closed.filter((t) => t.pnl > 0).reduce((a, t) => a + t.pnl, 0) / closed.filter((t) => t.pnl < 0).reduce((a, t) => a + t.pnl, 0) || 1); return (
{/* KPIs */}
= 0 ? "profit" : "loss"} />
{/* P&L distribution + Hourly heatmap */}
USD per trade
00:00 UTC peak window: 13:00–17:00 (London/NY overlap) 23:00 UTC
{/* Trades table */} onChange(e.target.value)} className="focus-ring" style={{ padding: "7px 28px 7px 12px", borderRadius: 8, border: "1px solid var(--line-strong)", background: "rgba(255,255,255,0.03)", color: "var(--text)", fontSize: 12, fontFamily: "inherit", outline: "none", appearance: "none", backgroundImage: "url(\"data:image/svg+xml;utf8,\")", backgroundRepeat: "no-repeat", backgroundPosition: "right 10px center", }}> {options.map((o) => )} ); } // ===================================================================== // RISK PAGE — drawdown, daily loss, circuit breaker, kill switches // ===================================================================== function RiskPage({ accent, scenario, activeExchange = "binance" }) { const s = SCENARIOS[scenario] || SCENARIOS.winning; const ex = EXCHANGES[activeExchange] || EXCHANGES.binance; const checks = [ { name: "Max concurrent positions", value: "1 of 1", status: "ok", detail: "No pyramiding — new signals queue while open" }, { name: "Daily loss limit", value: `−${s.dailyLoss}% / −3.0%`, status: s.dailyLoss >= 2.4 ? "warn" : "ok", detail: "Resets 00:00 UTC · trips circuit breaker" }, { name: "Signal deduplication", value: "60s window", status: "ok", detail: "Drops same signal_id seen within window" }, { name: "Min account balance", value: "$10,000 / $50", status: "ok", detail: "Rejects trades when below threshold" }, { name: "Active exchange reachable", value: ex.name + " · " + ex.rttMs + "ms", status: "ok", detail: "Health check against ACTIVE_EXCHANGE before every order" }, { name: "Pair available on " + ex.name, value: "BTC/USDT", status: "ok", detail: "Symbol catalogue cached at boot · " + ex.nativeSymbol + " native" }, { name: "Min notional ($10)", value: "$54.30 avg", status: "ok", detail: ex.name + " minimum order value · queried at startup" }, { name: "Max consecutive losses", value: "1 of 5", status: "ok", detail: "Auto-pauses bot when threshold hit" }, { name: "Max drawdown (kill switch)", value: `−${s.drawdown}% / −15.0%`, status: s.drawdown >= 10 ? "warn" : "ok", detail: "Permanent halt — requires manual restart" }, { name: "Stop-loss placement", value: ex.stopLoss, status: ex.oco ? "ok" : "warn", detail: ex.oco ? "OCO survives bot crashes" : "Conditional close — Kraken's OCO equivalent" }, ]; return (
{/* Top gauges */}
−{s.drawdown.toFixed(1)}%
peak equity $11,940 · current ${s.equity.toLocaleString()}
● Armed · monitoring
No trip events in last 7 days
} style={{ flex: 1, justifyContent: "center" }}>Manual halt } style={{ flex: 1, justifyContent: "center" }}>Reset
{/* Pre-trade checks table */}
{checks.map((c) => { const cfg = { ok: { c: "var(--profit)", icon: }, warn: { c: "var(--warn)", icon: }, err: { c: "var(--loss)", icon: } }[c.status]; return (
{cfg.icon}
{c.name} {c.value}
{c.detail}
); })}
{/* Position sizing */}
Formula
position_size = (account_balance × risk_pct){"\n"} {" "}÷ stop_loss_distance_pct{"\n\n"} # Current values{"\n"} account_balance = 10000.00{"\n"} risk_pct = 0.01 # 1%{"\n"} stop_loss_pct = 0.02 # 2%{"\n"} position_size = $5000.00 # ~0.078 BTC
Risk per trade
At 1% risk and 2% stop, you can lose 50 consecutive trades before hitting the 15% drawdown kill switch. Mathematically robust for noise-driven false signals.
); } function DailyLossMeter({ used, cap }) { const pct = Math.min(1, used / cap); const color = pct < 0.5 ? "var(--profit)" : pct < 0.8 ? "var(--warn)" : "var(--loss)"; return (
{[0.25, 0.5, 0.75].map((t) => (
))}
0% 1.5% 3.0% halt
); } function TripRow({ label, armed, count }) { return (
{label} {count && {count}}
); } function RiskBar({ label, value, cap, unit, color, reverse }) { const pct = reverse ? Math.min(1, cap / value) : Math.min(1, value / cap); return (
{label} {unit === "$" ? unit : ""}{value}{unit !== "$" ? unit : ""} / {unit === "$" ? unit : ""}{cap}{unit !== "$" ? unit : ""} {reverse ? "min" : "max"}
); } // ===================================================================== // BACKTESTER (v2 feature — preview) // ===================================================================== function BacktesterPage({ accent }) { const equity = upM(() => generateSeries(180, { seed: 5, start: 10000, drift: 0.0024, vol: 0.014 }), []); const benchmark = upM(() => generateSeries(180, { seed: 99, start: 10000, drift: 0.0010, vol: 0.018 }), []); return (
v2 PREVIEW
Backtesting is on the v2 roadmap. This is a preview of what historical strategy validation will look like.
{/* Config strip */}
{/* KPI strip */}
{/* Equity */} "$" + (v / 1000).toFixed(1) + "k"} /> {/* Monthly + drawdown */}
} style={{ marginTop: 8, justifyContent: "center" }}>Run backtest
); } function ConfigPill({ label, value }) { return (
{label}
{value}
); } function ParamSlider({ label, value, min, max, unit }) { const pct = ((value - min) / (max - min)) * 100; return (
{label} {value}{unit}
); } // ===================================================================== // ALERTS / TELEGRAM PAGE // ===================================================================== function AlertsPage({ accent }) { const events = [ { evt: "trade_opened", enabled: true, preview: "🟢 OPENED: LONG BTC/USDT @ 64210.50 | Size: 0.0084 | SL: 62926.30" }, { evt: "trade_closed", enabled: true, preview: "🔴 CLOSED: BTC/USDT @ 64055.40 | P&L: +$28.97 (+0.55%)" }, { evt: "stop_loss_triggered",enabled: true, preview: "⛔ STOP LOSS: BTC/USDT @ 63812.50 | Loss: −$101.82" }, { evt: "daily_limit_hit", enabled: true, preview: "🚨 DAILY LOSS LIMIT HIT: −3.02% | Trading halted" }, { evt: "signal_rejected", enabled: true, preview: "⚠️ SIGNAL REJECTED: Position already open" }, { evt: "error", enabled: true, preview: "❌ ERROR: ccxt.NetworkError — connection reset" }, { evt: "daily_summary", enabled: true, preview: "📊 DAILY SUMMARY: Trades: 7 | P&L: +$132.18 | Win rate: 86%" }, { evt: "bot_startup", enabled: true, preview: "🤖 BOT STARTED: Mode: TESTNET | Exchange: Binance | Balance: $10,000.00" }, ]; const cmds = [ { cmd: "/status", desc: "Bot state, open positions, daily P&L" }, { cmd: "/balance", desc: "Current account balance from exchange" }, { cmd: "/today", desc: "Today's trade summary" }, { cmd: "/stop", desc: "Emergency halt — closes positions, stops trading" }, { cmd: "/resume", desc: "Resume trading after manual halt" }, ]; return (
{/* Connection status */} ● Connected}>
}>Send test message }>Reconnect } style={{ marginLeft: "auto" }}>Open chat
{/* Events */}
{events.map((e) => (
{e.evt}
{e.preview}
))}
{cmds.map((c) => (
{c.cmd}
{c.desc}
))}
Only the configured TELEGRAM_CHAT_ID can execute commands. All others are silently ignored.
{/* Daily summary preview */}
AT
Auto-Trader Bot
bot · last seen now
📊 DAILY SUMMARY
2026-05-15 · UTC

Trades: 7
Wins: 6 · Losses: 1
Win rate: 86%
Net P&L: +$132.18 (+1.13%)
Max drawdown: −0.42%
); } function Field2({ label, value, mono }) { return (
{label}
{value}
); } // ===================================================================== // LOGS PAGE — structured JSON viewer // ===================================================================== function LogsPage({ accent }) { const [level, setLevel] = upS("All"); const [type, setType] = upS("All"); const types = [...new Set(LOGS.map((l) => l.type))]; const filtered = LOGS.filter((l) => (level === "All" || l.level === level.toUpperCase()) && (type === "All" || l.type === type) ); // Mock hourly volume data const vol = upM(() => Array.from({ length: 24 }, () => Math.floor(Math.random() * 80) + 20), []); const counts = { INFO: LOGS.filter((l) => l.level === "INFO").length, DEBUG: LOGS.filter((l) => l.level === "DEBUG").length, WARN: LOGS.filter((l) => l.level === "WARN").length, ERROR: LOGS.filter((l) => l.level === "ERROR").length, }; return (
{/* Stats + volume */}
{/* Filters */} }>Download log
} padded={false}>
{filtered.map((l, i) => )}
); } function LogLine({ log }) { const [open, setOpen] = upS(false); const levelCfg = { INFO: { c: "#60A5FA", bg: "rgba(96,165,250,0.10)" }, DEBUG: { c: "var(--text-3)", bg: "rgba(255,255,255,0.03)" }, WARN: { c: "var(--warn)", bg: "rgba(245,166,35,0.10)" }, ERROR: { c: "var(--loss)", bg: "rgba(242,63,92,0.10)" }, }[log.level]; const t = fmtTime(log.t); return (
setOpen(!open)} style={{ padding: "6px 24px", display: "flex", gap: 12, cursor: "pointer", borderLeft: "2px solid " + levelCfg.c, lineHeight: 1.5 }}> {t} {log.level} {log.type} {log.msg} {open && Object.keys(log.ctx).length > 0 && (
{Object.entries(log.ctx).map(([k, v]) => (
{k}: {JSON.stringify(v)}
))}
)}
); } // ===================================================================== // ROADMAP / BUILD PAGE — project task board, v1/v2/v3 timeline, changelog // ===================================================================== function RoadmapPage({ accent }) { const total = TASKS.length; const done = TASKS.filter((t) => t.status === "done").length; const wip = TASKS.filter((t) => t.status === "in-progress").length; const pend = TASKS.filter((t) => t.status === "pending").length; const pct = Math.round((done / total) * 100); return (
{/* Spec banner */}
spec v3.0 · MVP V1.1 · Dubai
aminbassam/Auto-Trader · last updated 2026-05-15 · {total} tasks across {TASK_CATEGORIES.length} categories
}>Open repo }>Download spec.json
{/* KPI strip with progress card */}
{/* Task board grouped by category */}
{TASK_CATEGORIES.map((cat) => { const tasks = TASKS.filter((t) => t.cat === cat.id); if (!tasks.length) return null; const catDone = tasks.filter((t) => t.status === "done").length; return (
{cat.label} {catDone} / {tasks.length}
{tasks.map((t) => )}
); })}
{/* Roadmap timeline v1/v2/v3 */}
{ROADMAP.map((r) => )}
{/* Changelog */}
{CHANGELOG.map((c, i) => (
{c.version} {c.date} {c.agent}
{c.summary}
    {c.highlights.map((h, j) => (
  • · {h}
  • ))}
))}
); } function CategoryProgressStack() { const total = TASKS.length; return (
{TASK_CATEGORIES.map((cat) => { const done = TASKS.filter((t) => t.cat === cat.id && t.status === "done").length; const w = (done / total) * 100; if (w === 0) return null; return
; })}
{TASK_CATEGORIES.map((cat) => { const tasks = TASKS.filter((t) => t.cat === cat.id); if (!tasks.length) return null; const done = tasks.filter((t) => t.status === "done").length; return ( {cat.label} {done}/{tasks.length} ); })}
); } function TaskCard({ task, color }) { const statusCfg = { "done": { c: "var(--profit)", bg: "rgba(25,229,166,0.06)", lbl: "done", icon: }, "in-progress": { c: "var(--accent)", bg: "rgba(0,212,170,0.08)", lbl: "in flight", icon: }, "pending": { c: "var(--text-3)", bg: "rgba(255,255,255,0.025)", lbl: "pending", icon: }, }[task.status]; return (
{statusCfg.icon}
{task.id} {statusCfg.lbl}
{task.task}
); } function RoadmapColumn({ phase }) { const accentColor = phase.status === "in-progress" ? "var(--accent)" : phase.status === "planned" ? "#60A5FA" : "#A78BFA"; return (
{phase.v.toUpperCase()}
{phase.name}
{phase.timeline}
{phase.current && ● NOW}
    {phase.features.map((f, i) => (
  • · {f}
  • ))}
); } Object.assign(window, { SignalsPage, TradesPage, RiskPage, BacktesterPage, AlertsPage, LogsPage, RoadmapPage, });