FX trading dashboard.
A live FX dashboard reading Sera's reference rates + multi-source mid + executable depth across 60+ stablecoin pairs. Read-only — no wallet, no signer, no notional commitment. Useful as an internal tool for traders, an external "data product," or a public marketing surface.
A web page showing live FX rates for any subset of Sera's pairs, with deviation-from-external-mid in basis points, sortable, refreshing every 60s. Pair this with charting (Lightweight Charts, Recharts) for a full trading dashboard.
Prerequisites
- Node 18+, any React framework
sera-mcprunning somewhere reachable from your Node backend- (Optional)
SERA_HISTORY_DBset to enable time-series — needed for the chart panel
What the dashboard shows
- Live grid of all 60+ pairs with: Sera reference rate, multi-source external mid, deviation in bps, last update.
- Deals tab showing only pairs where
find_dealsreturns positive edge ≥ 25 bps. - Spread radar for triangular arb across a fiat basket of your choice.
- (Optional) Chart panel showing rolling rate history per pair when
SERA_HISTORY_DBis enabled.
Steps
1Backend route — proxy MCP calls
Browser fetches read-only data from your Node backend. Your backend calls the MCP. Cache responses for 60s — every page-load otherwise costs you a fresh upstream round-trip, and rates don't move that fast anyway.
import { spawnSeraMcp } from './mcpClient';
const sera = spawnSeraMcp({ env: { SERA_NETWORK: 'mainnet' } });
const cache = new Map();
async function cached(key: string, ttlMs: number, fn: () => Promise<any>) {
const hit = cache.get(key);
if (hit && hit.exp > Date.now()) return hit.val;
const val = await fn();
cache.set(key, { val, exp: Date.now() + ttlMs });
return val;
}
export async function GET_grid() {
return cached('grid', 60_000, () => sera.call('find_deals', { min_bps: 0 }));
}
export async function GET_deals() {
return cached('deals', 60_000, () => sera.call('find_deals', { min_bps: 25 }));
}
export async function GET_radar(req) {
const currencies = req.query.basket.split(',');
return cached(`r:${currencies.join(',')}`, 60_000,
() => sera.call('spread_radar', { currencies, min_bps: 25 }));
}
2Frontend grid component
Sortable table. Color the deviation column by sign + magnitude. Refresh every 60s.
import { useEffect, useState } from 'react';
export function RateGrid() {
const [rows, setRows] = useState<any[]>([]);
useEffect(() => {
const tick = async () => {
const r = await fetch('/api/grid').then(r => r.json());
setRows(r.deals.sort((a, b) => b.edge_bps - a.edge_bps));
};
tick();
const id = setInterval(tick, 60_000);
return () => clearInterval(id);
}, []);
return (
<table>
<thead><tr>
<th>Pair</th><th>Sera</th><th>External mid</th><th>Δ bps</th>
</tr></thead>
<tbody>
{rows.map(r => (
<tr key={r.pair}>
<td>{r.pair}</td>
<td>{r.sera_rate}</td>
<td>{r.external_mid}</td>
<td style={{color: r.edge_bps > 0 ? '#3DE8A0' : '#FF6B6B'}}>
{r.edge_bps.toFixed(1)}
</td>
</tr>
))}
</tbody>
</table>
);
}
3Spread radar tab — triangular arb scanner
async function loadRadar(basket: string[]) {
const r = await fetch(`/api/radar?basket=${basket.join(',')}`).then(r => r.json());
return r.triangles; // [{ a, b, c, edge_bps, direction }, ...]
}
Visualize triangles as arrows on a 2D layout, or just list them sorted by edge_bps. Anything >100 bps is suspicious — verify with a fresh probe.
4(Optional) Add a chart panel
If you set SERA_HISTORY_DB on the MCP, every fx_rate call gets logged. After a few hours of dashboard runtime, you can plot sera.fx_history({ pair, hours }) series with any chart library.
const series = await sera.call('fx_history', { pair: 'USD/JPY', hours: 24 });
// [{ ts, rate }, ...]
5Deploy
This runs anywhere — Vercel, Cloudflare Pages, Railway. The only gotcha is your backend needs to spawn sera-mcp as a long-lived process. On serverless platforms, run sera-mcp as a separate service (Fly.io, Render, or a tiny VPS) and have your serverless functions call it over a private RPC.
Common gotchas
- Cache properly.
find_dealsprobes ~60 markets in parallel — calling it on every page load is wasteful. Rates barely move in 60s; cache them. - External-mid sources are rate-limited. Frankfurter and friends throttle. The
multi_source_midtool already deals with this; don't bypass it. - Pairs change. Trading pairs come and go. Don't hardcode the universe — read it fresh from
get_marketsorlist_currencies.
Next: Prediction market on FX uses Sera's reference rate as an oracle, or treasury rebalancer for the same data feeding a real action.