feat: alerts rule builder + destinations UI, portfolio charts + accuracy ledger
This commit is contained in:
1 parent
94a7f1dfd9
commit
5927eec465
2 files changed
+125
-80
No files matched your search
+49
-44
@@ -1,7 +1,8 @@
|
||||
import { createResource, For } from "solid-js";
|
||||
import { createResource, For, Show } from "solid-js";
|
||||
import { api } from "../lib/api";
|
||||
import { Bar } from "solid-chartjs";
|
||||
import { Chart, registerables } from "chart.js";
|
||||
import { StatCard, Skeleton } from "../components/ui";
|
||||
Chart.register(...registerables);
|
||||
|
||||
export default function Portfolio() {
|
||||
@@ -9,35 +10,37 @@ export default function Portfolio() {
|
||||
const [acc] = createResource(() => api.accuracy());
|
||||
const chartData = () => ({
|
||||
labels: (risk()?.concentration || []).map((c) => c.ticker),
|
||||
datasets: [{ label: "Weight", data: (risk()?.concentration || []).map((c) => +(c.weight * 100).toFixed(1)) }],
|
||||
datasets: [{ label: "Weight %", data: (risk()?.concentration || []).map((c) => +(c.weight * 100).toFixed(1)) }],
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<h1>Portfolio Risk + Accuracy Ledger</h1>
|
||||
<div class="fs-grid">
|
||||
<div class="fs-card">
|
||||
<h3>Concentration</h3>
|
||||
<Bar data={chartData()} options={{ responsive: true }} />
|
||||
<ul><For each={risk()?.warnings || []}>{(w) => <li>{w}</li>}</For></ul>
|
||||
<div class="fs-page-head"><h1>Portfolio Risk + Accuracy Ledger</h1><p class="fs-muted">Computed from your watchlist.</p></div>
|
||||
<Show when={!risk.loading && !acc.loading} fallback={<Skeleton rows={4} />}>
|
||||
<div class="fs-grid">
|
||||
<div class="fs-card">
|
||||
<h3>Concentration</h3>
|
||||
<Show when={(risk()?.concentration || []).length} fallback={<p class="fs-muted">Add tickers to your watchlist to see concentration.</p>}>
|
||||
<Bar data={chartData()} options={{ responsive: true }} />
|
||||
</Show>
|
||||
<For each={risk()?.warnings || []}>{(w) => <p>⚠️ {w}</p>}</For>
|
||||
</div>
|
||||
<StatCard title="Beta vs index" value={risk() ? String(risk()!.beta) : "…"}>
|
||||
<h3 style={{ "margin-top": "12px" }}>Correlation heatmap</h3>
|
||||
<CorrHeatmap matrix={risk()?.correlation || {}} />
|
||||
</StatCard>
|
||||
<div class="fs-card">
|
||||
<h3>Agent accuracy</h3>
|
||||
<table class="fs-table">
|
||||
<thead><tr><th>Agent</th><th>Calls</th><th>Resolved</th><th>Hit %</th></tr></thead>
|
||||
<tbody>
|
||||
<For each={acc()?.agents || []}>
|
||||
{(a) => <tr><td>{a.agent}</td><td>{a.calls}</td><td>{a.resolved}</td><td>{(a.hit_rate * 100).toFixed(0)}%</td></tr>}
|
||||
</For>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fs-card">
|
||||
<h3>Beta vs index</h3>
|
||||
<p style={{ "font-size": "24px" }}>{risk()?.beta ?? "…"}</p>
|
||||
<h3>Correlation heatmap</h3>
|
||||
<CorrHeatmap matrix={risk()?.correlation || {}} />
|
||||
</div>
|
||||
<div class="fs-card">
|
||||
<h3>Agent accuracy</h3>
|
||||
<table class="fs-table">
|
||||
<thead><tr><th>Agent</th><th>Calls</th><th>Resolved</th><th>Hit %</th></tr></thead>
|
||||
<tbody>
|
||||
<For each={acc()?.agents || []}>
|
||||
{(a) => <tr><td>{a.agent}</td><td>{a.calls}</td><td>{a.resolved}</td><td>{(a.hit_rate * 100).toFixed(0)}%</td></tr>}
|
||||
</For>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -50,23 +53,25 @@ function CorrHeatmap(props: { matrix: Record<string, Record<string, number>> })
|
||||
return bg;
|
||||
};
|
||||
return (
|
||||
<div class="fs-scroll"><table class="fs-table">
|
||||
<thead><tr><th></th><For each={keys()}>{(k) => <th>{k}</th>}</For></tr></thead>
|
||||
<tbody>
|
||||
<For each={keys()}>
|
||||
{(r) => (
|
||||
<tr>
|
||||
<td><strong>{r}</strong></td>
|
||||
<For each={keys()}>
|
||||
{(c) => {
|
||||
const v = props.matrix[r]?.[c] ?? 0;
|
||||
return <td style={{ background: cell(v) }} title={`${r}/${c} = ${v.toFixed(2)}`}>{v.toFixed(2)}</td>;
|
||||
}}
|
||||
</For>
|
||||
</tr>
|
||||
)}
|
||||
</For>
|
||||
</tbody>
|
||||
</table></div>
|
||||
<Show when={keys().length} fallback={<p class="fs-muted">No correlation data yet.</p>}>
|
||||
<div class="fs-scroll"><table class="fs-table">
|
||||
<thead><tr><th></th><For each={keys()}>{(k) => <th>{k}</th>}</For></tr></thead>
|
||||
<tbody>
|
||||
<For each={keys()}>
|
||||
{(r) => (
|
||||
<tr>
|
||||
<td><strong>{r}</strong></td>
|
||||
<For each={keys()}>
|
||||
{(c) => {
|
||||
const v = props.matrix[r]?.[c] ?? 0;
|
||||
return <td style={{ background: cell(v) }} title={`${r}/${c} = ${v.toFixed(2)}`}>{v.toFixed(2)}</td>;
|
||||
}}
|
||||
</For>
|
||||
</tr>
|
||||
)}
|
||||
</For>
|
||||
</tbody>
|
||||
</table></div>
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user