Current section
Files
Jump to
Current section
Files
lib/schemas/event.ex
defmodule Polymarket.Schemas.Event do
@moduledoc """
An event returned by the Gamma REST API (e.g. `GET /events/keyset`).
An event groups one or more `Polymarket.Schemas.Market`s under a single
question/title (e.g. an NBA game with its various betting markets). The Gamma
API delivers its keys in `camelCase`; `from_attrs/1` normalises them to the
`snake_case` fields below.
The `markets`, `series`, `tags`, and `event_metadata` relations are always
returned by the keyset endpoint and are parsed into their respective schemas.
Every scalar field in the Gamma `Event` OpenAPI schema is mapped (covering both
the keyset and single-event endpoints). The nested relational objects in that
spec (`bestLines`, `collections`, `eventCreators`, `externalPartners`,
`internalUsers`, `sport`, `teams`, `templates`, the `*Optimized` image objects,
...) are intentionally omitted; only `markets`, `series`, `tags`, and
`eventMetadata` are modelled as embedded relations.
"""
use TypedEctoSchema
alias Polymarket.JsonUtil
alias Polymarket.Schemas.Coerce
alias Polymarket.Schemas.EventMetadata
alias Polymarket.Schemas.Market
alias Polymarket.Schemas.Series
alias Polymarket.Schemas.Tag
@primary_key false
@derive Jason.Encoder
typed_embedded_schema do
# Identity / description
field(:id, :string)
field(:ticker, :string)
field(:slug, :string)
field(:title, :string)
field(:description, :string)
field(:resolution_source, :string)
field(:category, :string)
field(:subcategory, :string)
field(:series_slug, :string)
field(:sort_by, :string)
field(:gmp_chart_mode, :string)
field(:image, :string)
field(:icon, :string)
field(:updated_by, :string)
field(:created_by, :string)
field(:color, :string)
field(:country_name, :string)
field(:election_type, :string)
field(:neg_risk_market_id, :string)
field(:subtitle, :string)
field(:featured_image, :string)
field(:carousel_map, :string)
field(:disqus_thread, :string)
field(:estimated_value, :string)
field(:template_variables, :string)
field(:last_highlight, :string)
field(:last_highlight_type, :string)
# Status flags
field(:active, :boolean)
field(:closed, :boolean)
field(:archived, :boolean)
field(:new, :boolean)
field(:featured, :boolean)
field(:restricted, :boolean)
field(:cyom, :boolean)
field(:deploying, :boolean)
field(:pending_deployment, :boolean)
field(:comments_enabled, :boolean)
field(:enable_neg_risk, :boolean)
field(:neg_risk_augmented, :boolean)
field(:neg_risk, :boolean)
field(:enable_order_book, :boolean)
field(:automatically_active, :boolean)
field(:cumulative_markets, :boolean)
field(:requires_translation, :boolean)
field(:show_all_outcomes, :boolean)
field(:show_market_images, :boolean)
# The Gamma API emits `estimateValue` as a boolean `false` (not a number);
# `estimated_value` is a separate string field (in the Identity block above).
field(:estimate_value, :boolean)
field(:automatically_resolved, :boolean)
field(:cant_estimate, :boolean)
field(:is_template, :boolean)
# Counts / volume / liquidity
field(:comment_count, :integer)
field(:featured_order, :integer)
field(:neg_risk_fee_bips, :integer)
field(:parent_event_id, :integer)
field(:tweet_count, :integer)
field(:competitive, :float)
field(:liquidity, :float)
field(:liquidity_amm, :float)
field(:liquidity_clob, :float)
field(:open_interest, :float)
field(:volume, :float)
field(:volume24hr, :float)
field(:volume1wk, :float)
field(:volume1mo, :float)
field(:volume1yr, :float)
# Dates
field(:start_date, :utc_datetime_usec)
field(:end_date, :utc_datetime_usec)
field(:creation_date, :utc_datetime_usec)
field(:created_at, :utc_datetime_usec)
field(:updated_at, :utc_datetime_usec)
field(:closed_time, :utc_datetime_usec)
field(:published_at, :utc_datetime_usec)
field(:deploying_timestamp, :utc_datetime_usec)
field(:start_time, :utc_datetime_usec)
field(:event_date, :date)
field(:last_highlight_at, :utc_datetime_usec)
field(:scheduled_deployment_timestamp, :utc_datetime_usec)
# Sports / live-event metadata (only populated for sports-market events)
field(:live, :boolean)
field(:ended, :boolean)
field(:elapsed, :string)
field(:period, :string)
field(:score, :string)
field(:turn_provider_id, :string)
field(:event_week, :integer)
field(:game_id, :integer)
field(:rescheduled_from_game_id, :integer)
field(:spreads_main_line, :float)
field(:totals_main_line, :float)
field(:finished_timestamp, :utc_datetime_usec)
# Array fields
field(:sub_events, {:array, :string})
field(:tag_labels, {:array, :string})
field(:tag_slugs, {:array, :string})
embeds_one(:event_metadata, EventMetadata)
embeds_many(:markets, Market)
embeds_many(:series, Series)
embeds_many(:tags, Tag)
end
@required [:id, :slug]
@doc """
Builds an `Event` from the raw (JSON-decoded) Gamma attributes. Keys may be in
`camelCase` (atom or string); they are normalised to the `snake_case` fields
and each value is coerced to its field type. 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
attrs = JsonUtil.snake_case_keys(attrs)
%__MODULE__{
id: attrs["id"],
ticker: attrs["ticker"],
slug: attrs["slug"],
title: attrs["title"],
description: attrs["description"],
resolution_source: attrs["resolution_source"],
category: attrs["category"],
subcategory: attrs["subcategory"],
series_slug: attrs["series_slug"],
sort_by: attrs["sort_by"],
gmp_chart_mode: attrs["gmp_chart_mode"],
image: attrs["image"],
icon: attrs["icon"],
updated_by: attrs["updated_by"],
created_by: attrs["created_by"],
color: attrs["color"],
country_name: attrs["country_name"],
election_type: attrs["election_type"],
neg_risk_market_id: attrs["neg_risk_market_id"],
subtitle: attrs["subtitle"],
featured_image: attrs["featured_image"],
carousel_map: attrs["carousel_map"],
disqus_thread: attrs["disqus_thread"],
estimated_value: attrs["estimated_value"],
template_variables: attrs["template_variables"],
last_highlight: attrs["last_highlight"],
last_highlight_type: attrs["last_highlight_type"],
active: attrs["active"],
closed: attrs["closed"],
archived: attrs["archived"],
new: attrs["new"],
featured: attrs["featured"],
restricted: attrs["restricted"],
cyom: attrs["cyom"],
deploying: attrs["deploying"],
pending_deployment: attrs["pending_deployment"],
comments_enabled: attrs["comments_enabled"],
enable_neg_risk: attrs["enable_neg_risk"],
neg_risk_augmented: attrs["neg_risk_augmented"],
neg_risk: attrs["neg_risk"],
enable_order_book: attrs["enable_order_book"],
automatically_active: attrs["automatically_active"],
cumulative_markets: attrs["cumulative_markets"],
requires_translation: attrs["requires_translation"],
show_all_outcomes: attrs["show_all_outcomes"],
show_market_images: attrs["show_market_images"],
estimate_value: attrs["estimate_value"],
automatically_resolved: attrs["automatically_resolved"],
cant_estimate: attrs["cant_estimate"],
is_template: attrs["is_template"],
comment_count: Coerce.to_int(attrs["comment_count"]),
featured_order: Coerce.to_int(attrs["featured_order"]),
neg_risk_fee_bips: Coerce.to_int(attrs["neg_risk_fee_bips"]),
parent_event_id: Coerce.to_int(attrs["parent_event_id"]),
tweet_count: Coerce.to_int(attrs["tweet_count"]),
competitive: Coerce.to_float(attrs["competitive"]),
liquidity: Coerce.to_float(attrs["liquidity"]),
liquidity_amm: Coerce.to_float(attrs["liquidity_amm"]),
liquidity_clob: Coerce.to_float(attrs["liquidity_clob"]),
open_interest: Coerce.to_float(attrs["open_interest"]),
volume: Coerce.to_float(attrs["volume"]),
volume24hr: Coerce.to_float(attrs["volume24hr"]),
volume1wk: Coerce.to_float(attrs["volume1wk"]),
volume1mo: Coerce.to_float(attrs["volume1mo"]),
volume1yr: Coerce.to_float(attrs["volume1yr"]),
start_date: Coerce.to_datetime(attrs["start_date"]),
end_date: Coerce.to_datetime(attrs["end_date"]),
creation_date: Coerce.to_datetime(attrs["creation_date"]),
created_at: Coerce.to_datetime(attrs["created_at"]),
updated_at: Coerce.to_datetime(attrs["updated_at"]),
closed_time: Coerce.to_datetime(attrs["closed_time"]),
published_at: Coerce.to_datetime(attrs["published_at"]),
deploying_timestamp: Coerce.to_datetime(attrs["deploying_timestamp"]),
start_time: Coerce.to_datetime(attrs["start_time"]),
event_date: Coerce.to_date(attrs["event_date"]),
last_highlight_at: Coerce.to_datetime(attrs["last_highlight_at"]),
scheduled_deployment_timestamp: Coerce.to_datetime(attrs["scheduled_deployment_timestamp"]),
live: attrs["live"],
ended: attrs["ended"],
elapsed: attrs["elapsed"],
period: attrs["period"],
score: attrs["score"],
turn_provider_id: attrs["turn_provider_id"],
event_week: Coerce.to_int(attrs["event_week"]),
game_id: Coerce.to_int(attrs["game_id"]),
rescheduled_from_game_id: Coerce.to_int(attrs["rescheduled_from_game_id"]),
spreads_main_line: Coerce.to_float(attrs["spreads_main_line"]),
totals_main_line: Coerce.to_float(attrs["totals_main_line"]),
finished_timestamp: Coerce.to_datetime(attrs["finished_timestamp"]),
sub_events: attrs["sub_events"],
tag_labels: attrs["tag_labels"],
tag_slugs: attrs["tag_slugs"],
event_metadata: Coerce.embed(EventMetadata, attrs["event_metadata"]),
markets: Coerce.embed_list(Market, attrs["markets"]),
series: Coerce.embed_list(Series, attrs["series"]),
tags: Coerce.embed_list(Tag, attrs["tags"])
}
|> Coerce.require_fields(@required)
end
end