# ad-inventory-forecast

A production-grade Python library for predicting ad inventory (impressions) using ensemble forecasting with automatic model selection.

## Installation

```bash
pip install .
# or editable install:
pip install -e .
```

**Dependencies:** numpy, pandas, scipy, statsmodels, scikit-learn, fastdtw

**Optional:** prophet, xgboost, pmdarima, lightgbm

## Quick Start

```python
import pandas as pd
from ad_inventory_forecast import AutoForecaster

# Load your data (must have DatetimeIndex)
df = pd.read_csv('impressions.csv', parse_dates=['date'], index_col='date')

# Fit and predict
model = AutoForecaster()
model.fit(df['impressions'])
forecast = model.predict(horizon=30)

print(forecast)
#             predicted_impressions  lower_bound  upper_bound
# 2025-01-01              125000.0     98000.0    152000.0
# 2025-01-02              127500.0     99000.0    156000.0
# ...
```

## Models

### AutoForecaster

Automatically analyzes data and selects the best model. **Recommended for most use cases.**

```python
from ad_inventory_forecast import AutoForecaster

model = AutoForecaster(
    strategy='fast',     # 'fast', 'balanced', or 'thorough'
    horizon=30,          # Expected forecast horizon
    verbose=True         # Print model selection details
)
model.fit(y)
forecast = model.predict(horizon=30)

# See what was selected
print(model.advice_.selected_model)
print(model.advice_.selection_reasoning)
```

### InventoryForecaster

Growth-Seasonality Decomposition model. Separates trend, seasonality, and residuals.

```python
from ad_inventory_forecast import InventoryForecaster

model = InventoryForecaster(
    model='auto',              # 'additive', 'multiplicative', or 'auto'
    trend='auto',              # 'linear', 'exponential', 'damped', or 'auto'
    seasonal_periods=[7],      # Weekly seasonality for daily data
    trend_dampening=True,      # Dampen trend extrapolation (recommended)
    dampen_factor=0.98,        # Dampening rate (0.9-0.99)
    robust=True                # Robust to outliers
)
model.fit(y)
forecast = model.predict(horizon=30)
```

### ETSForecaster

Exponential Smoothing (Holt-Winters) with automatic component selection.

```python
from ad_inventory_forecast.models import ETSForecaster

model = ETSForecaster(
    trend='auto',           # 'add', 'mul', or None
    damped_trend=True,      # Dampen trend (recommended)
    seasonal='auto',        # 'add', 'mul', or None
    seasonal_periods=7      # Seasonal cycle length
)
model.fit(y)
forecast = model.predict(horizon=30)
```

### SARIMAForecaster

Seasonal ARIMA with automatic order selection.

```python
from ad_inventory_forecast.models import SARIMAForecaster

model = SARIMAForecaster(
    auto=True,              # Auto-select (p,d,q)(P,D,Q,m)
    seasonal_periods=7      # Seasonal period
)
model.fit(y)
forecast = model.predict(horizon=30)
```

### AnalogEventForecaster

"Copy model" for event-driven forecasting. Copies historical event profiles and scales to current trend.

```python
from ad_inventory_forecast import AnalogEventForecaster

model = AnalogEventForecaster(
    source_window=('2024-02-01', '2024-02-14'),  # Historical event
    target_window=('2025-02-01', '2025-02-14'),  # Future event
    use_dtw=True,           # Dynamic Time Warping alignment
    scale_by_trend=True     # Scale by growth trend
)
model.fit(y)
forecast = model.predict()
```

## Model Selection & Tuning

### ModelAdvisor

Analyzes data characteristics and recommends models.

```python
from ad_inventory_forecast import ModelAdvisor

advisor = ModelAdvisor(verbose=True)
advice = advisor.analyze(y)

print(advice.data_analysis)           # Data characteristics
print(advice.selected_model)          # Recommended model
print(advice.selection_reasoning)     # Why
for rec in advice.recommendations:
    print(f"{rec.model_name}: {rec.suitability}")
```

### AutoTuner

Walk-forward cross-validation for model comparison.

```python
from ad_inventory_forecast import AutoTuner

tuner = AutoTuner(
    candidates=[model1, model2, model3],
    metric='smape',         # 'smape', 'mase', 'mape', 'rmse'
    n_folds=3,
    horizon=14
)
result = tuner.select_best(y)
best_model = result.best_model
```

## Diagnostics & Robustness

The library includes automatic diagnostics for high-volatility series:

```python
model = AutoForecaster(verbose=True)
model.fit(y)

# Access diagnostics
analysis = model.advice_.data_analysis
print(f"Volatility: {analysis.volatility.volatility_level}")  # low/medium/high
print(f"CV: {analysis.volatility.coefficient_of_variation:.1%}")

if analysis.structural_break.detected:
    print(f"Warning: {analysis.structural_break.message}")
```

**Volatility-based interval widening:**

| CV Range | Level | Interval Multiplier |
|----------|-------|---------------------|
| < 20% | Low | 1.0x |
| 20-35% | Medium | 1.25x |
| > 35% | High | 1.5x |

## Metrics

```python
from ad_inventory_forecast import (
    calculate_smape,   # Symmetric MAPE (handles zeros)
    calculate_mase,    # Mean Absolute Scaled Error
    calculate_mape,    # Mean Absolute Percentage Error
    calculate_rmse,    # Root Mean Squared Error
    calculate_mae      # Mean Absolute Error
)

error = calculate_smape(actual, predicted)
```

## Demographic Reconciliation

For hierarchical forecasts with demographic segments:

```python
from ad_inventory_forecast import DemographicReconciler

reconciler = DemographicReconciler(method='top_down')
reconciled = reconciler.reconcile(total_forecast, segment_proportions)
```

## Data Requirements

**Input:** `pandas.Series` with `DatetimeIndex`

**Minimum history:**
- Daily data: 30+ days (180+ recommended)
- Weekly data: 26+ weeks
- Monthly data: 12+ months

**Output:** `pandas.DataFrame` with columns:
- `predicted_impressions`: Point forecast
- `lower_bound`: Lower confidence bound (95%)
- `upper_bound`: Upper confidence bound (95%)

## Package Structure

```
ad_inventory_forecast/
├── models/
│   ├── decomposition.py   # InventoryForecaster
│   ├── ets.py             # ETSForecaster
│   ├── sarima.py          # SARIMAForecaster
│   ├── analog.py          # AnalogEventForecaster
│   ├── auto.py            # AutoForecaster
│   └── trend.py           # Trend models
├── core/
│   ├── advisor.py         # ModelAdvisor
│   ├── diagnostics.py     # Volatility & structural break detection
│   ├── validation.py      # Data validation
│   └── preprocessing.py   # Data transforms
├── backtesting/
│   ├── metrics.py         # Error metrics
│   └── validator.py       # Walk-forward validation
├── seasonality/
│   ├── extraction.py      # STL/MSTL decomposition
│   └── fourier.py         # Fourier seasonality
├── tuning/
│   └── auto_tuner.py      # Model selection
├── reconciliation/
│   └── demographic.py     # Hierarchical reconciliation
└── utils/
    ├── dtw.py             # Dynamic Time Warping
    └── synthetic.py       # Synthetic data generation
```

## License

MIT
