defmodule Polymarket.Schemas.BestBidAskEvent do @moduledoc """ Best bid/ask event. """ use TypedEctoSchema alias Polymarket.Schemas.BestBidAskEvent alias Polymarket.Schemas.Coerce @primary_key false @derive Jason.Encoder typed_embedded_schema do field(:market, :string) field(:asset_id, :string) field(:best_bid, :float) field(:best_ask, :float) field(:spread, :float) # Unix epoch in milliseconds, delivered as a string (e.g. "1779660673012"). field(:timestamp, :integer) field(:event_type, :string) end @required [:market, :asset_id, :best_bid, :best_ask, :spread, :timestamp, :event_type] @doc """ Builds a `BestBidAskEvent` from a decoded (atom-keyed) websocket message. Constructs the struct directly — no `Ecto.Changeset` — for the websocket hot path. Numeric fields (delivered as JSON strings) are coerced via `Polymarket.Schemas.Coerce`. Returns `{:error, {:missing_fields, fields}}` when a required field is absent. """ @spec from_attrs(map()) :: {:ok, t()} | {:error, {:missing_fields, [atom()]}} def from_attrs(attrs) do %BestBidAskEvent{ market: attrs[:market], asset_id: attrs[:asset_id], best_bid: Coerce.to_float(attrs[:best_bid]), best_ask: Coerce.to_float(attrs[:best_ask]), spread: Coerce.to_float(attrs[:spread]), timestamp: Coerce.to_int(attrs[:timestamp]), event_type: attrs[:event_type] } |> Coerce.require_fields(@required) end end