defmodule Polymarket.Schemas.LastTradePriceEvent do @moduledoc """ Last trade price event. Reports the price, size and side of the most recent trade executed against an asset. """ use TypedEctoSchema alias Polymarket.Schemas.Coerce alias Polymarket.Schemas.LastTradePriceEvent @primary_key false @derive Jason.Encoder typed_embedded_schema do field(:market, :string) field(:asset_id, :string) field(:price, :float) field(:size, :float) # Maker fee in basis points, delivered as a string (e.g. "0"). field(:fee_rate_bps, :integer) field(:side, :string) field(:transaction_hash, :string) # Unix epoch in milliseconds, delivered as a string (e.g. "1782553200051"). field(:timestamp, :integer) field(:event_type, :string) end @required [:market, :asset_id, :price, :size, :side, :transaction_hash, :timestamp, :event_type] @doc """ Builds a `LastTradePriceEvent` 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 %LastTradePriceEvent{ market: attrs[:market], asset_id: attrs[:asset_id], price: Coerce.to_float(attrs[:price]), size: Coerce.to_float(attrs[:size]), fee_rate_bps: Coerce.to_int(attrs[:fee_rate_bps]), side: attrs[:side], transaction_hash: attrs[:transaction_hash], timestamp: Coerce.to_int(attrs[:timestamp]), event_type: attrs[:event_type] } |> Coerce.require_fields(@required) end end