Pure normalization and join helpers for Polymarket activity data.
This module operates on plain maps. It does not fetch from the Data API
or the CLOB; the caller pipes raw activity events (typically from
Polymarket.Data.get_activity/2) and CLOB fills (from
PolymarketClob.API.Account.get_trades/3) into these helpers.
All input keys are looked up in both camelCase string form (the shape Polymarket's Data API returns) and snake_case string form (the shape the CLOB and several internal callers use). Atom keys are also accepted as a convenience for in-Elixir construction.
Match semantics
cost_for_order/3, revenue_for_order/3, and redemption_for_market/2
sum USDC sizes across every matching event. This is a deliberate
divergence from the bot's extract_activity_cost/3 which returns the
first match: GTC orders that partially fill across multiple takers
produce multiple activity rows, and summing is the correct total.
All three return nil when no event matches, distinguishing
"no activity yet" from "events that summed to 0". The bot uses 0.0
for the same case; the SDK chose nil to keep "missing data" and
"actual zero" semantically separate.
Activity ↔ fills join
Polymarket's /activity endpoint identifies each TRADE by
transactionHash, not orderHash. To find the activity row for a
given CLOB order id, we resolve tx_hash from the fills list first
and then match against activity.transactionHash. When no fills list
is available, cost_for_order/3 and revenue_for_order/3 fall back
to a best-effort match against legacy orderHash / taker_order_id
fields. Pass an empty list [] for fills if all you have is the order
id and you want the fallback path.
Summary
Functions
Sums fee-inclusive USDC cost across every TRADE BUY event matching
order_id. Returns nil when no event matches.
Normalizes a list of raw /activity event maps into typed
Polymarket.Activity.Event structs.
Sums REDEEM payouts for condition_id. Returns nil when no event
matches.
Sums fee-inclusive USDC revenue across every TRADE SELL event
matching order_id. Returns nil when no event matches.
Types
Functions
Sums fee-inclusive USDC cost across every TRADE BUY event matching
order_id. Returns nil when no event matches.
Activity is matched to an order id by:
- Finding fills whose
order_idortaker_order_idequals the requestedorder_idand collecting theirtx_hash/transaction_hash. - Selecting activity events whose
transactionHashis in that set AND whosetype == "TRADE"ANDside == "BUY".
When step 1 yields nothing (e.g. caller did not supply fills), a
fallback matches activity rows directly by orderHash or
taker_order_id equal to order_id. The fallback is best-effort
only — Polymarket has historically populated transactionHash more
reliably than orderHash on /activity.
@spec normalize([raw_event()]) :: [Polymarket.Activity.Event.t()]
Normalizes a list of raw /activity event maps into typed
Polymarket.Activity.Event structs.
Unknown event types are still emitted with type: :unknown and the
full raw map preserved under :raw, so a caller iterating over a
large activity feed never loses events it did not anticipate.
@spec redemption_for_market([raw_event()], condition_id()) :: float() | nil
Sums REDEEM payouts for condition_id. Returns nil when no event
matches.
Activity rows are matched directly on conditionId / condition_id;
no fills join is needed because REDEEM events do not carry a CLOB
order id.
Sums fee-inclusive USDC revenue across every TRADE SELL event
matching order_id. Returns nil when no event matches.
Same join semantics as cost_for_order/3.