BrandSeasonalityIndex
Description
The Seasonality Index identifies brands with recurring seasonal revenue patterns, measuring both the overall concentration of revenue across months and the specific month in which revenue consistently peaks.
It answers two questions for every eligible brand:
- Is the business seasonal at all? →
gini_scoremeasures how concentrated revenue is across months. - If so, when is the peak? →
recurring_peak_monthidentifies the calendar month where revenue is most concentrated.
For example, a ski resort will show high revenue concentration in winter months (high gini_score) with recurring_peak_month = December. A tax preparation office will show concentration in early spring with recurring_peak_month = April. The index captures the pattern itself, but the underlying cause can vary: weather (ice cream shop), regulatory deadlines (tax preparer), academic calendars (school supply store), holidays (Christmas tree farm), or other factors.
**Layer 1 — Overall revenue concentration (gini_score)**
The Gini score measures how concentrated card revenue is across months. It ranges from 0 (revenue perfectly flat) to 1 (all revenue in a single month). A score of 0.4 or above indicates meaningful seasonal concentration and is the recommended threshold for identifying seasonal businesses.
Interpretation guide:
- **< 0.4**: Revenue roughly flat year-round — not considered seasonal (e.g. restaurant, gas station)
- **0.4–0.6**: Moderate seasonality — clear peak but revenue spread across several months (e.g. ice cream shop, garden center)
- **0.6–0.8**: Strong seasonality — most revenue concentrated in a short window (e.g. tax preparation, farm)
- **> 0.8**: Extreme concentration — nearly all revenue in 1–2 months (e.g. Halloween costume store, Christmas tree farm)
**Layer 2 — Recurring peak month (recurring_peak_month, peak_month_revenue_share)**
Identifies the recurring peak month where revenue is most concentrated. The peak month must be consistent across all observed periods, not just a single year.
Data Sources
Derived from historical card transaction revenue over a rolling 36-month observation window. Eligibility criteria are applied to ensure the seasonal signal is based on sufficient history and transaction volume to produce a reliable result. A brand is eligible if:
- It has at least $10,000 in card revenue in each 12-month period in the observation window.
- It has at least ~4 years of revenue history to calculate a reliable seasonal pattern.
Seasonality is measured directly from each brand's own revenue history rather than inferred from industry or geography. Not all tax preparers are seasonal, and not all ice cream shops are. A Miami ice cream shop may have flat revenue year-round while a Minnesota one peaks sharply in summer. This individual-level approach lets the data speak for each brand rather than relying on categorical assumptions.
type BrandSeasonalityIndex implements NodeFunctions {
id: UUID!
firstObservedDate: String
lastObservedDate: String
giniScore: Float
recurringPeakMonth: String
peakMonthRevenueShare: Float
internalId: String
internalBrandId: String
count(
field: String!
conditions: Conditions
): Int
countDistinct(
field: String!
conditions: Conditions
): Int
has(
field: String!
conditions: Conditions
): Boolean
sum(
field: String!
conditions: Conditions
): Int
min(
field: String!
conditions: Conditions
): Int
max(
field: String!
conditions: Conditions
): Int
avg(
field: String!
conditions: Conditions
): Float
collect(
field: String!
separator: String
conditions: Conditions
): String
minDateTime(
field: String!
conditions: Conditions
): DateTime
maxDateTime(
field: String!
conditions: Conditions
): DateTime
_fn: JSON
}
Fields
BrandSeasonalityIndex.id ● UUID! non-null scalar
BrandSeasonalityIndex.firstObservedDate ● String scalar
BrandSeasonalityIndex.lastObservedDate ● String scalar
BrandSeasonalityIndex.giniScore ● Float scalar
A score from 0 to 1 measuring how concentrated a business's card revenue is across months. Computed as the Gini coefficient of monthly revenue shares within each 12-month period, then taking the minimum across the three periods as a conservative floor. A higher score indicates greater revenue concentration across fewer months. Scores of 0.4 and above indicate meaningful seasonality.
BrandSeasonalityIndex.recurringPeakMonth ● String scalar
The month (e.g. "July") where card revenue is most concentrated across observed periods. Only populated when the peak month accounts for at least 20% of annual revenue in every observed period. NULL indicates no consistent seasonal peak was detected. Note: only the single dominant peak month is surfaced. Businesses with multiple seasonal peaks will show only the month with the highest recurring revenue share. As 65% of seasonal businesses at the recommended threshold have a single peak, this captures the majority of cases.
BrandSeasonalityIndex.peakMonthRevenueShare ● Float scalar
The revenue share of the peak calendar month, expressed as a value between 0 and 1. For example, a value of 0.20 means the peak month accounts for at least 20% of annual revenue in every observed period. When this value reaches 0.20, recurring_peak_month is populated. As a reference point, a perfectly flat non-seasonal business would show ~0.08 (1/12 ≈ 8%) for every month, so the 0.20 threshold means the peak month contributes at least 2.5x what a flat business would show.
BrandSeasonalityIndex.internalId ● String scalar
BrandSeasonalityIndex.internalBrandId ● String scalar
BrandSeasonalityIndex.count ● Int scalar
BrandSeasonalityIndex.count.field ● String! non-null scalar
BrandSeasonalityIndex.count.conditions ● Conditions input
BrandSeasonalityIndex.countDistinct ● Int scalar
BrandSeasonalityIndex.countDistinct.field ● String! non-null scalar
BrandSeasonalityIndex.countDistinct.conditions ● Conditions input
BrandSeasonalityIndex.has ● Boolean scalar
BrandSeasonalityIndex.has.field ● String! non-null scalar
BrandSeasonalityIndex.has.conditions ● Conditions input
BrandSeasonalityIndex.sum ● Int scalar
BrandSeasonalityIndex.sum.field ● String! non-null scalar
BrandSeasonalityIndex.sum.conditions ● Conditions input
BrandSeasonalityIndex.min ● Int scalar
BrandSeasonalityIndex.min.field ● String! non-null scalar
BrandSeasonalityIndex.min.conditions ● Conditions input
BrandSeasonalityIndex.max ● Int scalar
BrandSeasonalityIndex.max.field ● String! non-null scalar
BrandSeasonalityIndex.max.conditions ● Conditions input
BrandSeasonalityIndex.avg ● Float scalar
BrandSeasonalityIndex.avg.field ● String! non-null scalar
BrandSeasonalityIndex.avg.conditions ● Conditions input
BrandSeasonalityIndex.collect ● String scalar
BrandSeasonalityIndex.collect.field ● String! non-null scalar
BrandSeasonalityIndex.collect.separator ● String scalar
BrandSeasonalityIndex.collect.conditions ● Conditions input
BrandSeasonalityIndex.minDateTime ● DateTime scalar
BrandSeasonalityIndex.minDateTime.field ● String! non-null scalar
BrandSeasonalityIndex.minDateTime.conditions ● Conditions input
BrandSeasonalityIndex.maxDateTime ● DateTime scalar
BrandSeasonalityIndex.maxDateTime.field ● String! non-null scalar
BrandSeasonalityIndex.maxDateTime.conditions ● Conditions input
BrandSeasonalityIndex._fn ● JSON scalar
Interfaces
NodeFunctions interface
Member Of
BrandSeasonalityIndexEdge object