# Customer Lifetime Value (CLTV) Forecasting

Forecast how much each customer will spend in the months ahead, so you can invest where it matters most.

## Overview and Use Cases

CLTV AI Signals predicts how much each of your existing repeat customers will spend over a future window you choose, such as the next three, six, or twelve months. It reads your transaction history directly, with no pre-aggregation, and returns three things per customer: a **predicted CLTV**, a **percentile rank** against everyone else scored, and optionally a **churn probability** for the same window. Together they tell you who is worth investing in and who you are about to lose.

The solution offers two modeling approaches under one workflow:

**Probabilistic (BG/NBD combined with Gamma-Gamma)** learns the rhythm of how your customers shop: how often they tend to buy, how much they typically spend, and when their purchasing pattern suggests they may be drifting away. It fits customers with steady, repeatable buying habits, and it gives you a churn signal alongside the value forecast.

**AutoML (FLAML)** studies your full transaction history and finds the patterns that best predict future spend. You tune it toward one of two goals: accurate value predictions when planning budgets or forecasting revenue, or accurate customer rankings when you only need to identify top spenders. It performs best with a long, rich history to learn from.

**Common use cases**

- Allocate acquisition and retention budgets toward customers with the highest predicted future value
- Prioritize VIP service, loyalty perks, and personalized offers for top-percentile customers
- Surface churn-risk signals, with the probabilistic model, for proactive win-back campaigns
- Feed CLTV scores as input features into Next Best Action, lookalike, and propensity models
- Build segment tiers, Very High to Very Low, for differentiated lifecycle messaging


**Who benefits most:** Marketing analysts, CRM managers, and growth teams who need a forward-looking view of customer value without building a forecasting pipeline from scratch.

### How It Fits with the Other AI Signals

| **Question you're asking**  | **Use**  |
|  --- | --- |
| Who matters, based on past purchasing? | [RFM AI Signals](/ja/products/customer-data-platform/machine-learning/ai-signals/rfm-ai-signals): descriptive segments, no modeling required |
| How likely is this customer to do X? | [Propensity Scoring AI Signals](/ja/products/customer-data-platform/machine-learning/ai-signals/propensity-ai-signals): a probability per event you define |
| **How much will this customer be worth?** | **CLTV AI Signals (you are here)**: a value forecast and percentile rank |
| Which product should I recommend next? | [NBP AI Signals](/ja/products/customer-data-platform/machine-learning/ai-signals/nbp-ai-signals): a ranked list of products, services, or content |
| Which action should I take for this customer? | [NBA AI Signals](/ja/products/customer-data-platform/machine-learning/ai-signals/nba-ai-signals): a learned per-user policy across candidate actions |


***CLTV or Propensity Scoring?** These two are the pair readers regularly confuse, since both are predictive and both emit a per-customer score. CLTV answers how much: a predicted amount of spend over a horizon you choose. Propensity Scoring answers whether: a probability that one specific event happens. Pick CLTV when you are allocating budget by customer worth. Pick Propensity when you have a clear yes/no outcome to predict and want to set a threshold against campaign economics.*

*They compose rather than compete. A propensity score makes a useful input feature, and pairing CLTV with the churn probability from the probabilistic model tells you both how valuable a customer is and how likely you are to lose them, which is what identifies an urgent win-back target.*

## Quick Start

CLTV AI Signals runs as two functions on the Treasure AI ML Batch API:

- **`cltv_train`** fits the chosen model on your transaction history, evaluates it on the holdout period, writes metrics, and registers the model under `model_name`.
- **`cltv_predict`** loads that registered model and scores customers, writing per-customer predictions.


**You only need to set a handful of parameters.** `input_table`, `output_table`, and `model_name` are required. Add `user_column`, `amount_column`, and `timestamp_column` only if your columns are not already named `user_id`, `amount`, and `timestamp`. Everything else in this document has a working default, including `model_type`, which defaults to `bggg`.

The one default worth a second look is `prediction_period`, the forecast horizon in months. It defaults to 3, is set at training time only, and the training example below sets it to 6. [Choosing a Prediction Period](#choosing-a-prediction-period) covers how much history each horizon needs.

Security
Store your Treasure AI API key as a Workflow secret named `td.apikey`, never as a literal value in the workflow definition. Referencing it as a secret keeps the key out of version control, so teams can securely manage sensitive information. See [Setting Workflow Secrets from Treasure Console](/products/customer-data-platform/data-workbench/workflows/setting-workflow-secrets-from-td-console).

### Train

```yaml
# cltv_train.dig
# Step 1: Train, fit the model, evaluate it, and register it under `model_name`
+train:
  http>: https://ml-batch-api.treasuredata.com/v1/runs
  method: POST
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
    - X-TD-ML-SESSION-ID: ${session_id}
    - X-TD-ML-ATTEMPT-ID: ${attempt_id}
  store_content: true
  content:
    input_table: your_database.transactions
    output_table: your_database.cltv_metrics
    solution_name: cltv_train
    solution_arguments:
      model_name: "cltv_retail_v1"
      prediction_period: 6

+print_response:
  echo>: "Training job submitted. Response: ${http.last_content}"

# Poll until training completes. The API returns HTTP 408 while the job
# is running; Treasure Workflow retries until it receives HTTP 200.
+train_poll_status:
  http>: https://ml-batch-api.treasuredata.com/v1/runs/${JSON.parse(http.last_content)['id']}/status
  method: GET
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
```

**Expected output:** a metrics table in `your_database.cltv_metrics` scoring the model against the holdout period. Check it before you act on predictions. See [Reading Model Metrics](#reading-model-metrics). The trained model itself is registered under `model_name` and retrieved by `cltv_predict` automatically.

### Predict

```yaml
# cltv_predict.dig
# Step 2: Predict, load the registered model and score customers
+pred:
  http>: https://ml-batch-api.treasuredata.com/v1/runs
  method: POST
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
    - X-TD-ML-SESSION-ID: ${session_id}
    - X-TD-ML-ATTEMPT-ID: ${attempt_id}
  store_content: true
  content:
    input_table: your_database.transactions
    output_table: your_database.cltv_predictions
    solution_name: cltv_predict
    solution_arguments:
      model_name: "cltv_retail_v1"     # Must match training; the horizon is inherited from it

+print_response:
  echo>: "Prediction job submitted. Response: ${http.last_content}"

+pred_poll_status:
  http>: https://ml-batch-api.treasuredata.com/v1/runs/${JSON.parse(http.last_content)['id']}/status
  method: GET
  headers:
    - authorization: "TD1 ${secret:td.apikey}"
```

**Expected output:** one row per scored customer.

```
user_id     pcltv_6month   pcltv_6month_pctile
user_001    450.25         90
user_002    185.00         80
user_003    12.10          20
```

To create and schedule these workflows, see [Getting Started with Treasure Workflow](/products/customer-data-platform/data-workbench/workflows/getting-started-with-treasure-workflow).

## Model Configuration

Each CLTV AI Signals function takes two kinds of input: the transaction table you point it at, and the parameters you pass it. Both are documented under Data Preparation and Workflow Parameters below.

Each parameter table is complete on its own. Parameters shared across functions are repeated rather than cross-referenced, so you never have to assemble a configuration from two places.

### `cltv_train`

`cltv_train` fits the chosen model on your transaction history, evaluates it against a holdout period, and registers it for reuse.

#### Data Preparation

A transaction-level table. Each row is a single purchase by a single customer. No pre-aggregation is required; the solution summarizes internally.

| **Field**  | **Type**  | **Required**  | **Description**  |
|  --- | --- | --- | --- |
| `user_id` | STRING | Yes | Unique customer identifier. Column name is customizable via `user_column`. |
| `amount` | FLOAT | Yes | Monetary value of the transaction (quantity x unit price). Must be greater than zero. Use whichever currency or unit your business records; the model is unit-agnostic and every output is expressed in the same unit as this column. Column name is customizable via `amount_column`. |
| `timestamp` | STRING | Yes | Timestamp when the transaction occurred, for example `2024-02-03 00:06:16.342827`. Column name is customizable via `timestamp_column`. |


Example rows:

```
user_id      amount    timestamp
user_0001    450.00    2022-02-03 00:06:16.342827
user_0002    50.00     2022-02-08 13:30:43.348215
user_0003    2000.00   2022-02-09 05:09:35.244427
```

**Data requirements.** The **calibration window** referred to below is the historical stretch of your data the model learns from; [Data Splitting Strategies](#data-splitting-strategies) covers how it is derived.

- Each customer needs at least three transactions in the calibration window. Customers below the threshold are filtered out automatically and will not appear in the output. Controlled by `min_transactions`, whose floor is also 3.
- Negative or zero amounts produce errors. Clean refunds, voids, and promotional credits before training.
- Timestamps must be parseable. Mixed timezones in one column shift recency calculations and should be normalized upstream.
- Your total data horizon constrains `prediction_period`, especially for FLAML. See [Choosing a Prediction Period](#choosing-a-prediction-period).


#### Workflow Parameters

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source transaction table in dbname.table_name format. |
| `output_table` | string | — | Yes | Destination table for the metrics output. |
| `model_name` | string | — | Yes | Name to register the trained model under. The same name loads the model at prediction time. |
| `model_type` | string | bggg | No | Modeling approach: `bggg` for the probabilistic model or `flaml` for AutoML. See [Model Types](#model-types). |
| `user_column` | string | user_id | No | Name of the customer ID column in your input table. |
| `amount_column` | string | amount | No | Name of the transaction amount column. |
| `timestamp_column` | string | timestamp | No | Name of the transaction timestamp column. |
| `prediction_period` | int | 3 | No | Forecast horizon in months, typically 3, 6, or 12. Determines the calibration and holdout split, and the x in output column names. Set at training time only; `cltv_predict` inherits it from the registered model. Your total history caps it: the hard maximum is span - 1 month for `bggg` and span / 2 for `flaml`, where span is your data's date range in months. See [Choosing a Prediction Period](#choosing-a-prediction-period). |
| `min_transactions` | int | 3 | No | Minimum transactions per customer during the calibration period. Customers below this are dropped. The floor is 3. |
| `split_strategy` | string | auto-set | No | `single_cutoff` or `dual_cutoff`. Leave unset so it binds to the right strategy for your `model_type`. See [Data Splitting Strategies](#data-splitting-strategies). |
| `optimization_goal` | string | ranking | No | FLAML only. `value` minimizes prediction error (RMSE); `ranking` maximizes Gini. Ignored by `bggg`. |
| `time_budget` | int | 60 | No | FLAML only. Seconds allocated to the AutoML search. Longer budgets explore more configurations. Ignored by `bggg`. |


#### Output

A metrics table scoring the model against the **holdout period**, the slice of recent history withheld during training so predictions can be checked against what actually happened. The six churn metrics appear only when `model_type: bggg`, since it is the only approach that predicts churn.

| **Field**  | **Type**  | **Description**  |
|  --- | --- | --- |
| `rmse` | DOUBLE | Root Mean Squared Error of the CLTV predictions on the holdout period. |
| `mae` | DOUBLE | Mean Absolute Error of the CLTV predictions on the holdout period. |
| `label_gini` | DOUBLE | Gini coefficient computed on the true holdout labels. A ceiling on how rankable your base is. |
| `model_gini` | DOUBLE | Gini coefficient computed on the model's predictions. |
| `normalized_gini` | DOUBLE | `model_gini` / `label_gini`. Values closer to 1.0 indicate better ranking quality. |
| `churn_auc` | DOUBLE | **BGGG only.** Area under the ROC curve for the churn classifier. |
| `churn_precision` | DOUBLE | **BGGG only.** Precision at a 0.5 churn-probability threshold. |
| `churn_recall` | DOUBLE | **BGGG only.** Recall at a 0.5 churn-probability threshold. |
| `churn_f1` | DOUBLE | **BGGG only.** F1 score at a 0.5 churn-probability threshold. |
| `churn_brier_score` | DOUBLE | **BGGG only.** Brier score, measuring how well-calibrated the churn probabilities are. |
| `churn_ece` | DOUBLE | **BGGG only.** Expected Calibration Error, another calibration measure for churn probabilities. |


### `cltv_predict`

`cltv_predict` loads a registered model and scores customers.

#### Data Preparation

The same transaction-level table described in [`cltv_train`](#cltv_train), with the same schema and the same data requirements.

#### Workflow Parameters

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source transaction table in dbname.table_name format. |
| `output_table` | string | — | Yes | Destination table for the per-customer predictions. |
| `model_name` | string | — | Yes | Must match the name used at training time. |
| `model_type` | string | bggg | No | Must match the model type used at training time. |
| `user_column` | string | user_id | No | Name of the customer ID column in your input table. |
| `amount_column` | string | amount | No | Name of the transaction amount column. |
| `timestamp_column` | string | timestamp | No | Name of the transaction timestamp column. |


`prediction_period` is not a prediction parameter. The horizon is fixed when the model is trained, and `cltv_predict` inherits it from the registered model. To forecast over a different horizon, retrain with a new `prediction_period` under a new `model_name`.

#### Output

One row per scored customer. The churn column appears only when `model_type: bggg`.

| **Field**  | **Type**  | **Description**  |
|  --- | --- | --- |
| `user_id` | STRING | Customer identifier, carried forward from input. |
| `pcltv_xmonth` | DOUBLE | Predicted lifetime value over the next X months, where X is the `prediction_period` the model was trained with, for example `pcltv_6month`. |
| `pcltv_xmonth_pctile` | LONG | Percentile rank of the predicted value, 0 to 100. A value of 92 means the customer's predicted CLTV is higher than 92% of all scored customers. |
| `pchurn_xmonth` | DOUBLE | **BGGG only.** Probability the customer stops purchasing within the next X months. Range 0.0 to 1.0. |


A six-month run using FLAML:

```
user_id     pcltv_6month   pcltv_6month_pctile
user_001    450.25         90
user_002    185.00         80
user_003    12.10          20
```

The first customer is predicted to spend roughly 450 over the next six months, placing them in the top 10% of scored customers. The third ranks in the bottom 20%, a good candidate for low-cost engagement rather than expensive paid media.

**Using the predictions.** The output table can be joined to your customer table so `pcltv_xmonth` and `pcltv_xmonth_pctile` become attributes available to Master Segment rules. Build tiers from the percentile: a Very High Value segment as `pcltv_xmonth_pctile >= 80`, or a VIP segment as the top 1%. Those segments push to downstream activation destinations through standard CDP activation workflows and can feed Journeys, Triggers, and Personalization.

With BGGG, the churn column enables a second dimension. High predicted CLTV paired with high churn probability is your most urgent win-back target: valuable if retained, at risk of leaving. Schedule prediction to match your campaign cadence, weekly for fast-moving retail, monthly or quarterly for lower-frequency categories.

## Model Types

CLTV AI Signals supports two modeling approaches under the same configuration surface. Start with `bggg`, which is the default. Move to `flaml` when you need to optimize toward a specific goal and have the history to support it.

| **Model type**  | **Approach**  | **Best for**  | **Produces churn**  | **Main tradeoff**  |
|  --- | --- | --- | --- | --- |
| `bggg` | Probabilistic (BG/NBD + Gamma-Gamma) | Steady repeat-purchase patterns, shorter histories, interpretable parameters | Yes | Assumes regular purchase intervals |
| `flaml` | AutoML (FLAML) | Rich transaction history, goal-specific tuning | No | Needs a longer horizon, dual cutoff shortens the training window |


Each section below lists that model's complete parameter set, so you can configure it from one table.

### Probabilistic (`bggg`)

The probabilistic approach models two processes separately. The transaction process (BG/NBD) describes how often a customer purchases while still active and the probability they have silently churned. The monetary process (Gamma-Gamma) describes the typical value of each transaction. Combining them produces both a CLTV forecast and a churn probability for the same window.

This approach shines when your customers have steady, repeat-purchase patterns, such as specialty retail, grocery, or subscription-adjacent categories, because its assumptions about purchase intervals match those settings. It is also more interpretable than AutoML, since each fitted parameter has a clear behavioral meaning, which helps when explaining results to stakeholders.

#### Model-Specific Parameters

No parameters are specific to this model. `bggg` uses only the shared set.

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source transaction table in dbname.table_name format. |
| `output_table` | string | — | Yes | Destination table for function output. |
| `model_name` | string | — | Yes | Name to register the trained model under, and to load it at prediction time. |
| `model_type` | string | bggg | No | `"bggg"`. This is the default, so it can be omitted. |
| `user_column` | string | user_id | No | Name of the customer ID column in your input table. |
| `amount_column` | string | amount | No | Name of the transaction amount column. |
| `timestamp_column` | string | timestamp | No | Name of the transaction timestamp column. |
| `prediction_period` | int | 3 | No | Forecast horizon in months, typically 3, 6, or 12. Determines the calibration and holdout split, and the x in output column names. Set at training time only; `cltv_predict` inherits it from the registered model. Your total history caps it: the hard maximum is span - 1 month, where span is your data's date range in months. See [Choosing a Prediction Period](#choosing-a-prediction-period). |
| `min_transactions` | int | 3 | No | Minimum transactions per customer during the calibration period. The floor is 3. |
| `split_strategy` | string | single_cutoff | No | Binds to `single_cutoff` automatically for this model. Leave unset. See [Data Splitting Strategies](#data-splitting-strategies). |


This model ignores `optimization_goal` and `time_budget`, which are FLAML-only. It is the only approach that produces churn output: `pchurn_xmonth` in the prediction table and six churn metrics in the metrics table, both documented in [Model Configuration](#model-configuration).

#### Workflow Example

```yaml
solution_arguments:
  model_name: "cltv_retail_bggg_v1"
  model_type: "bggg"
  user_column: "user_id"
  amount_column: "amount"
  timestamp_column: "timestamp"
  prediction_period: 6
```

### AutoML (`flaml`)

The AutoML approach treats CLTV as a supervised learning problem. It builds RFM-style features from the calibration window and trains a regression model to predict spend in the holdout window, automatically searching across model families and hyperparameters within your `time_budget`.

The optimization goal determines what the search prioritizes. With `value`, FLAML minimizes RMSE, producing the most accurate predicted amounts, useful for budgeting or revenue forecasting. With `ranking`, it optimizes Gini, ordering customers correctly even if absolute amounts drift, useful when you only need to find your top customers. FLAML does not produce churn probabilities.

#### Model-Specific Parameters

Parameters in **bold** are specific to this model. The rest are shared with `bggg`.

| **Parameter**  | **Type**  | **Default**  | **Required**  | **Description**  |
|  --- | --- | --- | --- | --- |
| `input_table` | string | — | Yes | Source transaction table in dbname.table_name format. |
| `output_table` | string | — | Yes | Destination table for function output. |
| `model_name` | string | — | Yes | Name to register the trained model under, and to load it at prediction time. |
| `model_type` | string | bggg | No | `"flaml"`. Must be set explicitly, since the default is `bggg`. |
| `user_column` | string | user_id | No | Name of the customer ID column in your input table. |
| `amount_column` | string | amount | No | Name of the transaction amount column. |
| `timestamp_column` | string | timestamp | No | Name of the transaction timestamp column. |
| `prediction_period` | int | 3 | No | Forecast horizon in months, typically 3, 6, or 12. Determines the calibration and holdout split, and the x in output column names. Set at training time only; `cltv_predict` inherits it from the registered model. Your total history caps it: the hard maximum is span / 2, where span is your data's date range in months. See [Choosing a Prediction Period](#choosing-a-prediction-period). |
| `min_transactions` | int | 3 | No | Minimum transactions per customer during the calibration period. The floor is 3. |
| `split_strategy` | string | dual_cutoff | No | Binds to `dual_cutoff` automatically for this model. Overriding to `single_cutoff` causes temporal leakage. See [Data Splitting Strategies](#data-splitting-strategies). |
| **`optimization_goal`** | string | ranking | No | What the AutoML search optimizes. `value` minimizes prediction error (RMSE); `ranking` maximizes Gini and orders top customers correctly. |
| **`time_budget`** | int | 60 | No | Total seconds allocated to the FLAML model search. Longer budgets explore more model configurations. |


This model does not produce churn output. If you need a churn probability alongside the value forecast, use `bggg`.

#### Workflow Example

```yaml
solution_arguments:
  model_name: "cltv_retail_flaml_v1"
  model_type: "flaml"
  user_column: "user_id"
  amount_column: "amount"
  timestamp_column: "timestamp"
  prediction_period: 6
  optimization_goal: "ranking"
  time_budget: 60
```

## Relevant Topics

### Reading Model Metrics

`cltv_train` writes a metrics table scoring the model against the holdout period. Read it before acting on predictions.

**Judge on the metric you optimized for.** If you ran FLAML with `optimization_goal: ranking`, or you only need to identify top customers, `normalized_gini` is your headline number. If you ran `optimization_goal: value`, or you are forecasting revenue, read `rmse` and `mae` instead. A model can rank well and predict amounts poorly, or the reverse.

**Ranking quality.** `normalized_gini` is `model_gini` / `label_gini`, so it measures how close the model got to the best ranking your data allows rather than to a perfect ranking. Closer to 1.0 is better. Check `label_gini` too: if it is low, value is spread evenly across your base and there is less for ranking to exploit, which caps what any model can do.

**Value accuracy.** `rmse` and `mae` are both in the same unit as your `amount` column, so read them against average predicted CLTV to judge scale. An RMSE of 40 means something very different when average predicted CLTV is 50 than when it is 500. RMSE penalizes large misses more heavily than MAE, so a large gap between the two says a few customers are being badly mispredicted.

**Churn quality, BGGG only.** `churn_auc` tells you how well the model separates churners from non-churners, which is the right measure when you plan to rank by risk. `churn_brier_score` and `churn_ece` measure calibration, meaning whether a stated 0.30 probability really corresponds to a 30% churn rate. Lower is better for both. `churn_precision`, `churn_recall`, and `churn_f1` are computed at a fixed 0.5 threshold, so they say less if you intend to act on a different cutoff.

**Comparing models.** The pipeline reports the same CLTV metrics for both approaches, so running `bggg` and `flaml` against the same data and comparing holdout metrics is a fair test. Compare like with like, and remember that FLAML under dual cutoff is evaluated more honestly than a single-cutoff run.

### Data Splitting Strategies

The two model families consume your data differently, and that drives a different splitting strategy for each. You typically will not set this by hand, since the pipeline binds the right strategy to each `model_type` automatically, but understanding it makes the parameter choices clearer.

**Single cutoff**, used by BGGG, divides the timeline once into a calibration period, used to fit the model, and a holdout period, used to evaluate it. Probabilistic models do not need labeled training examples in the calibration period; they extract behavioral parameters directly from transaction summaries, so a single split is enough.

**Dual cutoff**, used by FLAML, divides the timeline at two points, T1 and T2, producing a feature window, a label window for training, and a final evaluation window. Machine learning models need labeled training examples, and a single cutoff would force the model to peek at outcomes that would not have been available at prediction time. The dual-cutoff structure prevents this temporal leakage by simulating two historical prediction points.

At T1, the training cutoff, features are computed using data only up to T1 and labels come from the window that follows. This creates a clean training example: given what we knew at T1, what happened next? At T2, the evaluation cutoff, features are recomputed up to T2 and actual outcomes after T2 are used for final evaluation, mirroring how the model behaves in production.

A worked example. Suppose your data spans January 2022 through December 2024, a thirty-six-month horizon, and you choose a six-month prediction period.

| **Strategy**  | **Window**  | **Range**  |
|  --- | --- | --- |
| Single cutoff | Calibration | Jan 2022 to Jun 2024, thirty months of fit data |
| Single cutoff | Holdout | Jul 2024 to Dec 2024 |
| Dual cutoff | Training features (to T1) | Jan 2022 to Dec 2023, twenty-four months of fit data |
| Dual cutoff | Training labels (T1 to T2) | Jan 2024 to Jun 2024 |
| Dual cutoff | Evaluation features (to T2) | Jan 2022 to Jun 2024 |
| Dual cutoff | Evaluation labels (after T2) | Jul 2024 to Dec 2024 |


Dual cutoff leaves you with less data to learn from, which is why FLAML benefits from longer histories. Plan for at least two years of training data.

### Choosing a Prediction Period

`prediction_period` is the parameter most likely to be set wrong, because its safe range depends on how much total history you have and which model you picked.

Two limits apply, and they are different in kind. There is a **hard maximum**, past which the split leaves nothing to train on and the run cannot produce a usable model, and there is a **practical range**, which is narrower and is what you should actually work within.

#### Maximum Prediction Period

The hard maximum is set by your **span**, the number of months your transaction data covers. It differs by model because the two split strategies consume different amounts of the timeline.

| **Model type**  | **Split strategy**  | **Maximum `prediction_period`**  | **What the split consumes**  |
|  --- | --- | --- | --- |
| `bggg` | Single cutoff | span - 1 month | The holdout takes one prediction period from the end of the timeline, so the calibration period is span - `prediction_period`. |
| `flaml` | Dual cutoff | span / 2 | Training labels and evaluation labels each take one prediction period, so the feature window is span - (2 x `prediction_period`). |


For a thirty-six-month span, that is 35 months for `bggg` and 18 months for `flaml`.

Treat these as the boundary of what will run, not as settings to use. At the `bggg` ceiling the model has one month of calibration data to fit behavioral parameters from; at the `flaml` ceiling the feature window is zero months wide. Both produce meaningless output long before you reach them.

#### Practical Range

With **single cutoff (BGGG)**, the holdout consumes one prediction period from the end of your timeline. A thirty-six-month horizon with a twelve-month prediction period still leaves twenty-four months to fit on.

With **dual cutoff (FLAML)**, the timeline gives up two prediction periods, one for training labels and one for evaluation labels. The same thirty-six-month horizon with a twelve-month prediction period leaves only twelve months of fit data, which is thin. Aim to keep at least two years for training.

If your horizon is short and you still want FLAML, shorten `prediction_period`, six months instead of twelve. Overriding `split_strategy` to `single_cutoff` is possible but introduces temporal leakage and makes the evaluation metrics unreliable. We do not recommend it.

Longer horizons also amplify uncertainty on their own terms. Predicting twelve-month CLTV is meaningfully harder than three-month CLTV because more can change in a customer's life and in your business. Treat long-horizon predictions as directional rather than precise.

## FAQs

### Getting Started

**What data do I need?** A transaction-level table with one row per purchase, containing a customer identifier, a positive amount, and a parseable timestamp. No pre-aggregation. Each customer needs at least three transactions in the calibration window to be scored.

**Which model type should I pick, BGGG or FLAML?** Start with BGGG, the default. It provides built-in churn prediction and works well with steady repeat-purchase patterns or shorter transaction histories. Move to FLAML if BGGG does not produce the results you need, or if you want to optimize explicitly for ranking accuracy or value accuracy. FLAML requires a longer history. If unsure, run both and compare holdout metrics; the pipeline reports the same CLTV metrics for each.

**How often should I retrain versus repredict?** Training is expensive and only needs to run when your customer base, product mix, or business model has shifted enough to change behavior, typically monthly or quarterly. Prediction is much cheaper and benefits from running weekly, so recently active customers get fresh scores. Splitting the stages lets you tune each cadence independently.

### Data and Quality

**Why are some of my customers missing from the prediction output?** The pipeline filters out any customer with fewer than three transactions in the calibration period, because the underlying models cannot fit reliable parameters with less. This is controlled by `min_transactions`, whose default and floor are both 3.

**Can I predict CLTV for prospective customers who have not purchased yet?** No. This solution is built for existing repeat customers. Prospect scoring uses different signals, such as ad clicks, page views, and cart activity, rather than transaction history, and is handled by separate models.

**What should I do about refunds and negative amounts?** Clean them before training. Negative or zero amounts produce errors. Net out refunds, voids, and promotional credits upstream so every row is a positive purchase value.

**Does timezone matter?** Yes. Mixed timezones in one timestamp column shift recency calculations, which both models depend on. Normalize timestamps upstream.

**How does data freshness affect predictions?** Stale transaction data pushes every customer toward looking inactive, which biases predictions downward and inflates apparent churn risk. Match your prediction schedule to your campaign cadence.

### Choosing a Model

**What is the difference between `optimization_goal: value` and `optimization_goal: ranking`?** `value` tells FLAML to minimize RMSE, so predicted amounts land as close as possible to true future spend. Use it for revenue forecasts and budgeting. `ranking` tells FLAML to maximize Gini, so your top customers sort to the top even if absolute amounts drift. Use it when you only need the top decile for a VIP campaign. The setting applies to FLAML only.

**Which model gives me churn probability?** Only BGGG. It produces `pchurn_xmonth` in the prediction table and six churn metrics in the metrics table. FLAML predicts value alone.

**Can I use CLTV for fixed-price subscriptions?** It is not designed for that. When monetary value is flat across customers, as with a single-price cell phone plan, CLTV collapses into a churn forecast. BGGG partially provides one, but churn is not its primary objective and a dedicated churn model will serve you better.

### Interpreting Results

**How do I tell whether my model is any good?** Read the metric matching what you optimized for. For ranking, `normalized_gini` closer to 1.0 means the model ranks nearly as well as your data allows. For value accuracy, read `rmse` and `mae` against average predicted CLTV for scale. See [Reading Model Metrics](#reading-model-metrics) for the full guide.

**Why do my dual-cutoff FLAML metrics look worse than my single-cutoff metrics?** Because the dual-cutoff metrics are honest. Single cutoff with FLAML produces temporal leakage, which inflates the numbers by letting the model see information from the future. The dual-cutoff figures are what you should expect in production, and the gap between the two is the size of the leakage.

**What is the largest `prediction_period` I can set?** It depends on your span, the number of months your transaction data covers, and on the model. `bggg` caps at span - 1 month; `flaml` caps at span / 2, because dual cutoff spends one prediction period on training labels and another on evaluation labels. Those are hard limits that leave almost no data to fit on, so stay well inside them. See [Choosing a Prediction Period](#choosing-a-prediction-period).

**How much should I trust a twelve-month forecast?** Less than a three-month one. Longer horizons amplify uncertainty because more can change in a customer's life and in your business. Treat long-horizon predictions as directional and revisit them more often.

**How accurate does the model need my history to be?** The probabilistic model needs enough purchase intervals per customer to estimate behavioral parameters reliably. FLAML under dual cutoff needs at least two years of total horizon for a six-month prediction period. Shorter horizons compress the training window and degrade evaluation reliability.

### Activation

**How do I activate CLTV scores in a campaign tool?** The output table carries `pcltv_xmonth` and `pcltv_xmonth_pctile` for every scored customer. Build segments directly from them, for example a Very High Value segment as percentile >= 80, or a VIP segment as the top 1%. Those segments push to downstream activation destinations through standard CDP activation workflows and can feed Journeys, Triggers, and Personalization.

**How do I find urgent win-back targets?** Use BGGG and combine the two outputs. High `pcltv_xmonth` paired with high `pchurn_xmonth` identifies customers who would be valuable if retained but are at risk of leaving. That intersection is where retention spend earns the most.

## Glossary

| **Term**  | **Definition**  |
|  --- | --- |
| **CLTV** | Customer Lifetime Value, the total revenue a single customer is expected to generate over a defined future window. |
| **Calibration period** | The historical window the model uses to learn customer behavior. |
| **Holdout period** | The future window held back during training and used to evaluate how well the model predicts. |
| **Prediction period** | The future window the model forecasts for, fixed at training time by `prediction_period`, for example 6 months. |
| **`pcltv_xmonth`** | The predicted amount a customer will spend over the prediction period. The x is replaced by the number of months. |
| **`pchurn_xmonth`** | The predicted probability a customer stops purchasing within the prediction period. Produced only by BGGG. |
| **Percentile rank** | Where a customer's predicted CLTV sits relative to all other scored customers, from 0 to 100. |
| **Single cutoff** | A splitting strategy that divides the timeline once into a calibration period and a holdout period. Used by BGGG. |
| **Dual cutoff** | A splitting strategy that divides the timeline at two points, T1 and T2, to produce a clean training example without temporal leakage. Used by FLAML. |
| **Temporal leakage** | When a model indirectly sees information that would not have been available at prediction time. Causes evaluation metrics to be artificially inflated. |
| **BG/NBD** | Beta-Geometric / Negative Binomial Distribution, a probabilistic model of customer purchase frequency and silent churn. |
| **Gamma-Gamma** | A probabilistic model of average transaction monetary value. Pairs with BG/NBD to produce CLTV forecasts. |
| **FLAML** | Fast and Lightweight AutoML, an open-source library that automatically searches model families and hyperparameters within a time budget. |
| **Optimization goal** | The metric FLAML's search optimizes. `value` minimizes prediction error (RMSE); `ranking` maximizes ranking quality (Gini). |
| **RMSE** | Root Mean Squared Error, average prediction error in the same unit as `amount`, penalizing large misses more heavily than small ones. |
| **MAE** | Mean Absolute Error, average prediction error in the same unit as `amount`, treating all misses proportionally. |
| **Gini coefficient** | A measure of how well predictions rank customers from highest to lowest value. Higher is better; 1.0 is a perfect ranking. |
| **Normalized Gini** | `model_gini` / `label_gini`. Values close to 1.0 mean the model ranks customers nearly as well as the true labels allow. |
| **Churn probability** | The probability a customer stops purchasing within the prediction period. Produced only by BGGG. |
| **Master Segment** | A Treasure AI CDP construct holding all customers scored by the model. Child Segments are subsets filtered by predicted CLTV percentile. |