DpExchange.Gemini.Socket (DpExchangeGemini v0.1.1)

Copy Markdown View Source

The venue's WebSocket connection — internal, and never named above the facade.

Speaks wss://ws.gemini.com, the API Gemini's current documentation describes. This is not the API the host adapter uses, and the reasoning is in docs/reference/gemini/websocket-api-replacement.md. In one line: the host's api.gemini.com/v2/marketdata still answers, but it is absent from the vendor's current documentation and from four years of its changelog, so a package published for other people to depend on should not be built on it.

Protocol

Subscription is an RPC-shaped frame naming streams:

{"method":"subscribe","params":["btcusd@bookTicker","ethusd@bookTicker"],"id":1}

and the venue acknowledges with {"id":1,"status":200}.

This package subscribes to @bookTicker and nothing else. That single stream carries best bid, best ask, and last trade price — everything a Core.Types.Quote needs, in one message per change.

Which is why there is no order-book machinery here

The host maintains a 182-line L2 book (gemini/l2_book.ex) whose entire purpose is to reconstruct a mid price from l2_updates deltas, because the endpoint it connects to offers no top-of-book message. This endpoint does. The book is not ported, and the moduledoc of the file that is not ported records why it existed — a mid computed from a single delta rather than the maintained book, which is the incident that created it.

A caller wanting depth calls get_order_book/2, which is a REST snapshot with the venue's own per-level timestamps.

Event time is nanoseconds

The E field is nanoseconds since the epoch, not milliseconds. The difference is a factor of a million: read as milliseconds, a 2026 timestamp lands in the year 58,000 and every staleness check passes forever. Converted once, here.

Summary

Functions

Subscribes the connection to @bookTicker for each symbol.

Functions

start_link(opts)

@spec start_link(keyword()) :: {:ok, pid()} | {:error, term()}

subscribe(socket, symbols)

@spec subscribe(pid(), [String.t()]) :: :ok | {:error, term()}

Subscribes the connection to @bookTicker for each symbol.

Returns {:error, :send_timeout} rather than exiting when the socket will not accept the frame — a caller can retry a batch, but it cannot recover from a linked exit it did not expect.

unsubscribe(socket, symbols)

@spec unsubscribe(pid(), [String.t()]) :: :ok | {:error, term()}