Lattice Grid Buy a licence

demo D261

Real-Time Trading Dashboard: One Market Feed in React

One market feed fans to a quotes grid and a movers grid narrowed by its own route, plus a price chart

createDataRouter · attach filter · priceFeed

This is a ready-made trading terminal: one market feed fans out to a quotes grid and a movers grid narrowed by its own route, with a price chart alongside. It shows a whole desk built from a single feed, ready to adapt to a real market source.

Building…
Loading a live grid…

This is the React version. The grid mounts through the createLatticeGrid adapter, which takes its configuration as ordinary props and hands back the live grid through a ref. The grid below is the same one every other tab runs.

The configuration

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.min.css">

<div id="app"></div>

<script type="module">
  import React from 'https://esm.sh/react@18';
  import { createRoot } from 'https://esm.sh/react-dom@18/client';
  import { createGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.esm.min.js';
  import { createLatticeReact } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/react.esm.min.js';
  import { createChart } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/charts.esm.min.js';
  import { createDataRouter } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/data-router.esm.min.js';
  import { MockWebSocket, priceFeed } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/mock-socket.esm.min.js';

  const { LatticeGrid, LatticeChart, useLatticeRouter, LatticeRouterProvider } =
    createLatticeReact({ React, createGrid, createChart, createDataRouter });

  const MONEY = { style: 'currency', currency: 'USD', decimals: 2 };
  const upDown = [
    { id: 'up', when: { op: 'gt', value: 0 }, style: { color: '#3ddc97' } },
    { id: 'down', when: { op: 'lt', value: 0 }, style: { color: '#ff6b6b' } },
  ];
  const columns = [
    { field: 'symbol', title: 'Symbol', layout: { width: 84, pin: 'start' } },
    { field: 'last', title: 'Last', type: 'number', format: MONEY },
    { field: 'chg', title: 'Chg', type: 'number', format: { decimals: 2 } },
    { field: 'bid', title: 'Bid', type: 'number', format: MONEY },
    { field: 'ask', title: 'Ask', type: 'number', format: MONEY },
  ];

  function App() {
    // overlap lets the one 'price' partition reach both grids.
    const router = useLatticeRouter({ key: 'type', rowKey: 'symbol', overlap: true });
    const [quotesGrid, setQuotesGrid] = React.useState(null);

    React.useEffect(() => {
      if (!router) return undefined;
      const socket = new MockWebSocket({ feed: priceFeed({ seed: 7 }), rate: 260 });
      socket.onmessage = (event) => {
        const message = JSON.parse(event.data);
        if (message.kind === 'snapshot') router.load(message.rows);
        else router.apply(message.changes);
      };
      return () => socket.close();
    }, [router]);

    return (
      <LatticeRouterProvider router={router}>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '14px' }}>
          <LatticeGrid
            route="price" rowKey="symbol" highlightOnChange={{ duration: 350 }} formatting={{ chg: upDown }}
            columns={columns} rows={[]} onGridReady={setQuotesGrid} style={{ height: '300px' }}
          />
          {/* The movers route reshapes its slice before it lands: a filter,
              then a sort, per route. */}
          <LatticeGrid
            route="price"
            routeOptions={{ filter: (row) => Math.abs(row.chg) >= 0.4, sort: { key: 'chg', dir: 'desc' } }}
            rowKey="symbol" highlightOnChange={{ duration: 350 }} formatting={{ chg: upDown }}
            columns={columns} rows={[]} style={{ height: '300px' }}
          />
        </div>
        {quotesGrid && (
          <LatticeChart grid={quotesGrid} type="bar" x="symbol" y="last" title="Last price by symbol" style={{ height: '220px', marginTop: '14px' }} />
        )}
      </LatticeRouterProvider>
    );
  }

  createRoot(document.getElementById('app')).render(<App />);
</script>