# `DpExchange.Core.Types.StakingReward`
[🔗](https://github.com/DistortionPoint/dp-exchange-core/blob/v0.3.120/lib/dp_exchange/core/types/staking_reward.ex#L1)

Rewards accrued on a staked position over a period.

## A reward is an aggregate, and the period is part of the value

Venues accrue staking rewards on their own schedule — Gemini typically once daily — and
report them aggregated over a window the caller asked for. `:amount` alone is therefore
meaningless: the same number is a good day or a poor quarter depending on the window.

So `:period_start` and `:period_end` are carried, and `:accrual_count` says how many
individual accruals the aggregate covers. A caller comparing venues has to compare over
the same window, and cannot do that unless the window travels with the number.

## The rate is the rate *at accrual*, not now

`:apy_pct` is what the position was earning while these rewards accrued, which is not
necessarily what `Types.StakingRate` reports today. Carrying it here is what lets a caller
reconcile a reward against the rate that produced it rather than against the current one.

# `t`

```elixir
@type t() :: %DpExchange.Core.Types.StakingReward{
  accrual_count: non_neg_integer() | nil,
  amount: Decimal.t(),
  apy_pct: Decimal.t() | nil,
  asset: String.t(),
  period_end: DateTime.t() | nil,
  period_start: DateTime.t() | nil,
  provider: atom(),
  provider_id: String.t() | nil
}
```

# `new`

```elixir
@spec new(keyword() | map()) :: t()
```

Builds a `t:t/0`, failing closed if a required field is absent or `nil`.

`@enforce_keys` guards presence, not `nil` — see `DpExchange.Core.Types.Validate`.

`:period_start` and `:period_end` are each a `DateTime` or `nil`: the window travels with the
number (see above), and a window a consumer cannot compare is no window.

Also raises on a negative `:amount` or `:apy_pct`, and on an `:accrual_count` that is not a
non-negative integer or `nil`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
