# Example: Revenue Monitoring with Detector Preprocessing
#
# This example demonstrates the preprocessing, weighting and detrending
# parameters shared by the mad / zscore / iqr detectors.
#
# Scenario:
# - Daily revenue varies widely (weekdays: $10K, weekends: $30K)
# - Absolute values don't indicate problems
# - CHANGES in revenue are what matter (sudden 50% drop = problem)
#
# Features demonstrated:
# - input_type: "changes" - detect on relative day-over-day changes
# - smoothing: "sma" - reduce noise before detection
# - window_weights: "exponential" + half_life - adapt to baseline shifts
#
# Parameter reference (invalid values fail fast when the detector is
# constructed at the start of the detect step — once per run, not at
# config load):
#
# input_type:
#   "values"           - raw values (default)
#   "changes"          - relative change: (v[t] - v[t-1]) / v[t-1]
#   "absolute_changes" - absolute change: v[t] - v[t-1]
#   "log_changes"      - log change: log(v[t]+1) - log(v[t-1]+1)
#
# smoothing: null (default) | "ema" (uses smoothing_alpha, default 0.3)
#            | "sma" (uses smoothing_window, default 10)
#   Smoothing is applied FIRST, then the input_type transformation.
#
# window_weights: null (uniform, default) | "exponential" | "linear"
#   exponential: w(age) = 0.5^(age / half_life)
#   linear:      w(age) = (window_size + 1 - age) / window_size
#   Weights are time-aware: they depend on a point's age on the time grid,
#   so data gaps don't compress the decay and seasonality groups share the
#   same recency horizon as global statistics.
#
# half_life: int (points) | duration string ("3d", "12h") | null
#   For exponential weights: the age at which a point's weight halves.
#   Duration strings are converted via the metric's data grid step.
#   Default when unset: max(window_size / 20, min_samples / 2).
#   (weight_decay is a deprecated alias: decay 0.95 ~ half_life 13.5 points;
#   mutually exclusive with half_life.)
#
# detrend: null (default) | "linear"
#   Estimates a robust linear trend over the window and projects every
#   window point to the current point along it before computing statistics.

name: daily_revenue_growth
description: Daily revenue with relative-change detection and smoothing
interval: "1day"

# Load daily revenue from database
query: |
  SELECT
    DATE(timestamp) as timestamp,
    SUM(amount) as value
  FROM sales
  WHERE timestamp >= '{{ dtk_start_time }}'
    AND timestamp < '{{ dtk_end_time }}'
  GROUP BY DATE(timestamp)
  ORDER BY timestamp

# Loading configuration
loading_start_time: "2024-01-01 00:00:00"
loading_batch_size: 90  # 90 days per batch

# Detector with preprocessing
detectors:
  - type: mad
    params:
      # PREPROCESSING: detect on relative day-over-day changes
      input_type: "changes"
      # Example: $10K -> $15K becomes +0.5 (+50%)
      #          $30K -> $33K becomes +0.1 (+10%)

      # SMOOTHING: reduce noise from daily fluctuations
      # (applied before the change transformation)
      smoothing: "sma"
      smoothing_window: 7  # 7-day moving average

      # WEIGHTING: adapt to seasonal baseline changes
      window_weights: "exponential"
      half_life: "14d"  # a point's weight halves every 14 days
      # Faster adaptation when the baseline shifts (e.g. holiday season)

      # Standard MAD parameters (threshold is in sigma-equivalents)
      threshold: 3.0
      window_size: 90  # 90 days of history
      min_samples: 30  # Need at least 30 valid days in the window

# Alert configuration
alerting:
  enabled: true
  timezone: "UTC"

  channels:
    - slack_finance
    - email_executives

  # Anomaly filtering
  min_detectors: 1
  direction: "any"  # Alert on unusual growth OR drop
  consecutive_anomalies: 2  # 2 consecutive days (reduces false positives)

  # Alert cooldown
  alert_cooldown: "1day"  # Max 1 alert per day
  cooldown_reset_on_recovery: true

  # Special alerts
  no_data_alert: true  # Alert if revenue data is missing!

# Example detection scenarios:
#
# Scenario 1: Absolute values vary, but changes are normal
# Mon: $10,000 -> Tue: $10,500 (+5%) -> Normal
# Tue: $10,500 -> Wed: $11,000 (+4.8%) -> Normal
# Result: No alerts (consistent growth)
#
# Scenario 2: Abnormal percentage change
# Thu: $11,500 -> Fri: $20,000 (+74%) -> ANOMALY
# Fri: $20,000 -> Sat: $21,000 (+5%) -> Normal again
# Result: Alert sent (unusual spike in growth)
#
# Scenario 3: Gradual baseline shift (seasonal)
# Week 1 avg: $12K/day ... Week 4 avg: $15K/day (holiday season)
# Result: No alerts - exponential weighting adapts to the new baseline
#
# Scenario 4: Sudden drop
# Mon: $15,000 -> Tue: $8,000 (-47%) -> ANOMALY
# Result: Alert sent after 2 consecutive anomalous days

# ---------------------------------------------------------------------------
# Recipe: metrics with a gradual trend (alert-spam killer)
#
# A slowly trending metric (e.g. sessions declining ~15% over a month)
# eventually drifts out of a uniform-window confidence interval and spams
# "below" alerts even though nothing sudden happened. Two tools fix this:
#
# - window_weights: exponential + half_life
#   The interval follows the recent level, adapting to the new normal
#   over roughly one half_life.
# - detrend: linear
#   A robust linear trend (split-median slope, outlier-resistant) is
#   estimated over the window and removed before computing statistics,
#   so the drift itself is no longer anomalous while sharp deviations
#   from the trend are still caught.
#
# Trade-off: a shorter half_life adapts faster but also "accepts" a real
# sustained degradation as the new normal - alerts still fire during
# roughly the first half_life of an incident.
#
# detectors:
#   - type: mad
#     params:
#       window_size: 8640        # 60 days of 10-min points
#       min_samples: 1000
#       seasonality_components: ["hour"]  # needs seasonality_columns: [hour]
#                                         # at metric level (or a custom column
#                                         # declared in query_columns.seasonality)
#       window_weights: exponential
#       half_life: "3d"          # adapt to the new normal over ~3 days
#       detrend: linear          # optional: also remove in-window trend
# ---------------------------------------------------------------------------

# Tags for filtering
tags:
  - finance
  - critical
  - revenue
