How to Build a Live Financial Dashboard with Phoenix LiveView and Streaming APIs
HexShift

HexShift @hexshift

About: Elixir & Phoenix enthusiast sharing advanced guides on Phoenix Framework, LiveView, WebSocket, Python and Tailwind. Helping devs build reactive, scalable apps with deep, practical insights.

Joined:
Apr 5, 2025

How to Build a Live Financial Dashboard with Phoenix LiveView and Streaming APIs

Publish Date: Jun 14
0 0

In volatile markets, every second counts. Your users don’t want to refresh—they want live prices, moving charts, and streaming data.

Phoenix LiveView makes this not just possible—but elegant.

No SPA. No JS frameworks. Just Elixir, sockets, and structured state.


Streaming Price Feeds in Real Time

Most financial APIs (Binance, Coinbase, Polygon.io) expose WebSocket feeds.

Use websockex to consume them:

WebSockex.start_link(
  "wss://stream.binance.com:9443/ws/btcusdt@trade",
  __MODULE__,
  %{}
)
Enter fullscreen mode Exit fullscreen mode

Each message is a trade or price event. Forward it to your app via:

Phoenix.PubSub.broadcast(MyApp.PubSub, "btc:usd", {:price_update, payload})
Enter fullscreen mode Exit fullscreen mode

Then in your LiveView:

def mount(_, _, socket) do
  Phoenix.PubSub.subscribe(MyApp.PubSub, "btc:usd")
  {:ok, assign(socket, latest_price: nil)}
end

def handle_info({:price_update, data}, socket) do
  {:noreply, assign(socket, latest_price: data["p"])}
end
Enter fullscreen mode Exit fullscreen mode

Boom: real‑time price updates, rendered automatically.


Live Streams: Constantly Updating Lists

Show recent trades with live_stream:

def handle_info({:trade, trade}, socket) do
  {:noreply,
   socket
   |> stream_insert(:trades, trade, at: 0)
   |> stream_delete(:trades, -1)}
end
Enter fullscreen mode Exit fullscreen mode
<ul id="trades" phx-update="stream">
  <%= for {id, trade} <- @streams.trades do %>
    <li id={id} class="flex justify-between text-sm">
      <span><%= trade.price %></span>
      <span><%= trade.qty %></span>
    </li>
  <% end %>
</ul>
Enter fullscreen mode Exit fullscreen mode

Use Tailwind for visual flair:

  • Green/red for price direction (text-green-600, text-red-600)
  • animate-pulse to show price volatility
  • overflow-y-auto h-60 to contain rapid updates

Resilient Data Layers with PubSub

Decouple WebSocket logic with a MarketDataServer GenServer:

defmodule MarketDataServer do
  use GenServer

  def start_link(pair), do: GenServer.start_link(__MODULE__, pair)

  def init(pair) do
    WebSockex.start_link(..., self(), ...)
    {:ok, %{pair: pair}}
  end

  def handle_info({:websocket, data}, state) do
    Phoenix.PubSub.broadcast(MyApp.PubSub, "market:#{state.pair}", {:tick, data})
    {:noreply, state}
  end
end
Enter fullscreen mode Exit fullscreen mode

Each LiveView subscribes only to the stream it cares about.


Initial Load: Historical Data

Pair live streaming with an HTTP seed on mount:

{:ok, resp} = Req.get("https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1m")
candles = parse_klines(resp.body)

{:ok, assign(socket, candles: candles)}
Enter fullscreen mode Exit fullscreen mode

Render the chart server-side on first load. Then push new points from the WebSocket stream.


Filters and User Interactions

Let users toggle symbols or timeframes using LiveView’s phx-click or phx-change.

<select name="pair" phx-change="change_pair">
  <option value="BTCUSDT">BTC</option>
  <option value="ETHUSDT">ETH</option>
</select>
Enter fullscreen mode Exit fullscreen mode
def handle_event("change_pair", %{"pair" => pair}, socket) do
  # unsubscribe, subscribe to new pair, reset assigns
  {:noreply, socket}
end
Enter fullscreen mode Exit fullscreen mode

No page reload. No JS. Just LiveView.


Real-Time Alerts

Let users define thresholds. Track them in a process or with Oban:

if price >= alert.threshold do
  Oban.insert!(%MyApp.Jobs.SendNotification{user_id: alert.user_id})
end
Enter fullscreen mode Exit fullscreen mode

In LiveView:

if alert_triggered do
  assign(socket, alert_message: "BTC > $30k 🚀", alert_triggered: true)
end
Enter fullscreen mode Exit fullscreen mode

Show it inline with:

<%= if @alert_triggered do %>
  <div class="bg-yellow-100 p-4 rounded shadow"> <%= @alert_message %> </div>
<% end %>
Enter fullscreen mode Exit fullscreen mode

Internal Tools: Dashboards & Monitoring

Use this same stack to build:

  • Admin UIs for monitoring trade flow
  • Fraud detection panels with live rule hits
  • Payment system monitors showing queue depth

All real-time. All server-driven. All scalable.


Why LiveView Wins for Financial UIs

  • ✅ WebSockets out of the box
  • ✅ Clean server-state model
  • ✅ Live updates via handle_info
  • ✅ Fast, minimal HTML updates
  • ✅ No JS frameworks or client state needed
  • ✅ Scales horizontally with Phoenix channels

Build experiences that feel native—but run in the browser.


Want to Go Pro?

Get the PDF:

Phoenix LiveView: The Pro’s Guide to Scalable Interfaces and UI Patterns

  • Live dashboards
  • Streaming data
  • Tailwind-styled components
  • WebSocket + REST integrations
  • Patterns for async UX and alerts

Whether it’s crypto, equities, or internal ops — build it right. Build it live. Build it with Phoenix.

Comments 0 total

    Add comment