Back

Integrate Elixir Phoenix with Binance and Coinbase Websocket (1)

As a physical Crypto Ticker producer, we know that stable and real-time data is very important for us and our customers, so our products L1 crypto ticker and Nixie bitcoin clock are powered by several professional platforms’ data sources. To ensure that we get the data in real time, we prefer using the WebSocket streams from Binance and Coinbase first.

In this article, we are going to show you how we built the Elixir application to get real-time updates from the Binance and Coinbase WebSocket streams.

Binance Websocket streams

Let’s get started with the Binance WebSocket streams. Actually, the awesome online book ‘Hands-on Elixir & OTP: Cryptocurrency trading bot’ written by Kamil Skowron has the details of how to stream live cryptocurrency prices from the Binance WSS. The code in the book is based on Elixir umbrella app, but for us, we are using Phoenix to build our app, so here we would adapt the code base to Phoenix.

1. Create a Phoenix project

mix phx.new x_sure_wss

2. Append WebSocket client module: WebSocket and the concurrent cache module: con_cache to the deps function inside the mix.exs file. And don’t forget to run mix deps.get to fetch these dependencies.

3. :con_cache an ETS based key/value storage for caching the data from WebSocket, we need to start it in lib/x_sure_wss/application.ex:

4. Connect Binance’s WebSocket Stream using the WebSockex module

As we want to receive multiple cryptocurrency trades and ticker streams and look at the Binance WebSocket documents, combined streams are accessed at /stream?streams=<streamName1>/<streamName2>/<streamName3> meet our requirements perfectly.

The trade streams for multiple cryptocurrencies is: wss://stream.binance.com:9443/stream?streams=btcusdt@trade/ethusdt@trade/bnbusdt@trade/solusdt@trade/adausdt@trade/xrpusdt@trade/dotusdt@trade/dogeusdt@trade/avaxusdt@trade

So define the XSureWss.Binance.BinanceWss (lib/x_sure_wss/binance/binance_wss.ex) module to use WebSockex to connect to Binance Websocket

defmodule XSureWss.Binance.BinanceWss do
  use WebSockex

  require Logger

  @stream_endpoint "wss://stream.binance.com:9443/stream?streams="

  def start_link(streams) do
    case WebSockex.start_link(
      "#{@stream_endpoint}#{streams}",
      __MODULE__,
      nil
    ) do
      {:ok, pid} ->
        Logger.info("Binance websocket started for #{streams}")
        {:ok, pid}
      {:error, reason} ->
        Logger.error("error: #{inspect(reason)}")
        {:error, %{msg: "#{inspect(reason)}"}}
    end

  end

  def terminate(reason, state) do
    Logger.warning("\nSocket Terminating:\n#{inspect reason}\n\n#{inspect state}\n")
    exit(:normal)
  end

  def handle_frame({_type, msg}, state) do
    case Jason.decode(msg) do
      {:ok, event} -> process_event(event)
      {:error, _} -> Logger.error("Unable to parse msg: #{msg}")
    end

    {:ok, state}
  end

  defp process_event(%{"data" => event, "stream"=> stream}) do
    Logger.debug(
      "Stream event received " <>
        "#{stream} #{inspect(event)}"
    )
    case event do
      %{"e" => "trade"} ->
        ConCache.put(:xsure_cache, stream, event["p"])
      %{"e" => "24hrTicker"} ->
        ConCache.put(:xsure_cache, stream, event["0"])
        Logger.debug("ticker event")
      _->
        Logger.debug("other steam event")
    end

  end
end

5. Start Binance WebSocket when Phoenix application starts

Define XSureWss.Binance.Streamer (lib/x_sure_wss/binance/binance_streamer.ex) the Phoenix Supervisor child_spec. Here we separate trade streams and ticker streams into 2 processes.

defmodule XSureWss.Binance.Streamer do

  alias XSureWss.Binance.BinanceWss
  alias XSureWss.TickerUtils

  def child_spec(_args) do
    # Specs for the Binance websocket.
    streams = [Enum.join(TickerUtils.binance_trade_streams, "/"), Enum.join(TickerUtils.binance_ticker_streams, "/")]
    children =
      for {stream, i} <- Enum.with_index(streams) do
        Supervisor.child_spec({BinanceWss, stream},
         id: {BinanceWss, "stream_#{i}"}
        )
      end

    # Spec for the supervisor that will supervise the Binance websocket.
    %{
      id: BinanceStreamerSupervisor,
      type: :supervisor,
      start: {Supervisor, :start_link, [children, [strategy: :one_for_one]]}
    }
  end

end

Append XSureWss.Binance.Streamer to application start

Run mix phx.server. If everything is OK, you will see the following streams logs

Source code

You can find the complete finished code in this GitHub repository. Please don’t forget to change the dev DB config to yours, otherwise, you will get the below Postgrex errors.

What’s Next

In the next article, we will show you how to integrate Coinbase WebSocket as well.

References

Leave a Reply

Applying the discount code, please wait...