Cartrack LTV & Pricing Engine

Platform documentation v1.0 · Last updated March 2026

1. Overview

The Cartrack Contract Intelligence Platform is a full-stack analytics application that provides four stakeholder dashboards covering Life Time Value determination, churn analytics, pricing optimisation, and model governance.

Sales & Marketing

Real-time pricing calculator, LTV by segment, cohort analytics, acquisition funnel, and target segment recommender to maximise profitable acquisition.

C-Suite Management

Portfolio KPIs, MRR waterfall, 36-month cashflow projection with confidence bands, unit economics by cohort, and contract drill-down.

Retentions & Collections

Preservation priority queue, churn risk distribution, bad billing EV decision model, and intervention outcomes tracking.

Model Governance

Model registry, calibration by decile, AUC trend, PSI feature drift monitoring, data quality dashboard, and audit log.

Tech Stack

LayerTechnologyPurpose
BackendFastAPI 0.110+Async REST API with OpenAPI docs at /docs
ORMSQLAlchemy 2.xModel definitions, session management, query layer
DatabaseSQLite (dev) / PostgreSQL (prod)Contract portfolio data store
Churn MLXGBoostGradient boosted trees for churn probability scoring
LTVNumPy + survival analysisDiscounted cashflow with Monte Carlo CI
PricingSciPy minimize_scalarConstrained optimisation of expected profit
FrontendStatic HTML + Plotly.jsGoldman Sachs terminal aesthetic dashboards
ConfigPydantic BaseSettingsEnvironment-variable driven configuration
Testingpytest + FastAPI TestClient233 unit + integration tests

2. Architecture

System Architecture

System Architecture — 4-layer stack: Client, FastAPI, Analytics Engine, Database
FIG 1 — Layered architecture: Client → API → Analytics → Database

Module Structure

Life Expectancy/
├── backend/
│   ├── main.py              FastAPI app entry point, lifespan, routers, static files
│   ├── config.py            Pydantic settings (discount rate, DB URL, etc.)
│   ├── database.py          SQLAlchemy engine, SessionLocal, Base, get_db()
│   ├── models/              SQLAlchemy ORM models (10 tables)
│   │   ├── contract.py      Core entity — full subscription lifecycle
│   │   ├── customer.py      Subscriber profile + credit band
│   │   ├── payment.py       Monthly billing records
│   │   ├── product.py       Subscription package catalogue
│   │   ├── cost.py          CostUpfront + CostMonthly line items
│   │   ├── hardware.py      Hardware unit + FitmentRecord
│   │   ├── support.py       Support tickets (incl. cancellation requests)
│   │   └── recovery.py      Vehicle theft + recovery events
│   ├── analytics/           Pure Python analytics modules
│   │   ├── ltv.py           LTV formula, Monte Carlo CI, portfolio aggregation
│   │   ├── churn.py         XGBoost churn scorer, preservation queue
│   │   ├── collections.py   Bad billing EV decision model
│   │   └── pricing.py       Constrained price optimisation
│   ├── api/                 FastAPI route handlers
│   │   ├── management.py    C-Suite endpoints (portfolio, waterfall, cohort, etc.)
│   │   ├── sales.py         Sales endpoints (pricing, LTV by segment, funnel)
│   │   ├── retentions.py    Retentions endpoints (queue, collections, notices)
│   │   └── governance.py    Governance endpoints (registry, drift, audit log)
│   └── data/
│       └── seed.py          Synthetic data generator (5,000 contracts)
├── frontend/
│   ├── assets/
│   │   ├── design-system.css  Goldman Sachs terminal CSS variables + components
│   │   └── api-client.js      API helpers, fmt utilities, Plotly dark theme
│   ├── index.html           Landing page + live KPI strip
│   ├── sales.html           Sales & Marketing dashboard
│   ├── management.html      C-Suite Management dashboard
│   ├── retentions.html      Retentions & Collections dashboard
│   ├── governance.html      Model Governance dashboard
│   └── wiki.html            This documentation page
├── tests/                   pytest test suite (233 tests)
├── pytest.ini
├── start.bat / start.sh     Quick-start scripts
└── CLAUDE.md                Project rules and context

Data Flow Pipeline

Data Flow Pipeline — Raw Sources → Ingestion → Database → Analytics → API → Dashboards
FIG 2 — End-to-end data flow: Sources → Ingest → Store → Compute → Serve → Render

3. Business Model

Subscription Mechanics

Cartrack sells vehicle tracking subscriptions on a recurring monthly fee basis. Each contract has a cancellation clause (minimum term, typically 36 months) that penalises early termination.

Revenue StreamDescriptionModel Treatment
Subscription MRRMonthly fee paid by active subscribersPrimary component of subscription_pv in LTV
Cancellation Penaltiespenalty_pv in LTV; ~65-70% collection rate assumed
Vehicle Recovery Fees~10× monthly fee on successful vehicle recoveryrecovery_pv in LTV; 3.6% annual theft rate, 72% recovery rate

Contract Lifecycle

Contract Lifecycle State Machine — Active, Bad Billing, Suspended, Cancelled, Recovery, HW Retrieval
FIG 3 — Contract state machine: subscription lifecycle from sale to cancellation

Cost Structure

CategoryTypeComponentsTypical Range (ZAF)
HardwareUpfrontDevice purchase / stock allocationR1,200 – R2,800
Fitment LabourUpfrontTechnician time: base_labour × complexity_multR180 – R500
Fitment TravelUpfrontdistance_km × R3.50 × complexity_multR18 – R700
CAC MarketingUpfrontDigital / ATL attributionR150 – R300
CAC SalesUpfrontAgent commissionR200 – R500
PlatformMonthlyServer, maps API, SIM dataR60 – R105
SupportMonthlyPro-rated cost per ticketR15 – R25
RepairsMonthlyHardware repair (avg. amortised)R0 – R50
CollectionsMonthlyAgent follow-up for bad billing~8% of monthly fee

Bad Billing Decision Framework

When a subscriber stops paying, three actions are evaluated each month using Expected Value maximisation:

P(resume payment) decays exponentially with time in bad billing:

Band multipliers:   A = 1.4  ·  B = 1.1  ·  C = 0.85  ·  D = 0.55  ·  unrated = 0.90
Bad Billing Decision Flow — EV model choosing between Continue, Cancel, and Retrieve Hardware
FIG 4 — Bad billing EV decision flow: monthly evaluation of three action paths

4. Data Schema

contracts

Core entity. One row per subscription contract.

ColumnTypeDescription
contract_idUUID PKUnique contract identifier
customer_idUUID FK→ customers.customer_id
product_idUUID FK→ products.product_id
country_codeCHAR(3)ISO-3166-1 alpha-3 (ZAF, KEN, NGA, TZA, ZMB)
sales_channelVARCHARdirect | dealer | corporate | online | broker
start_dateDATEContract origination date
cancellation_clause_monthsINTMinimum term in months (default 36, fleet 24)
monthly_feeNUMERIC(12,2)Recurring monthly subscription fee
statusVARCHARactive | bad_billing | suspended | cancelled
cancellation_dateDATEDate of termination (if cancelled)
cancellation_reasonVARCHARvoluntary_price_sensitivity | forced_non_payment | …
penalty_invoicedNUMERIC(12,2)Penalty amount billed on cancellation
penalty_collectedNUMERIC(12,2)Penalty amount actually collected
months_activeINTTenure in months (updated monthly)
vehicle_makeVARCHARVehicle manufacturer (Toyota, BMW, etc.)
distance_to_fitter_kmNUMERIC(8,2)Distance to nearest installation centre

customers

ColumnTypeDescription
customer_idUUID PKUnique customer identifier
customer_typeVARCHARindividual | fleet | corporate | dealer
country_codeCHAR(3)ISO country of domicile
acquisition_channelVARCHARChannel through which customer was acquired
first_contract_dateDATEDate of first ever contract with Cartrack
credit_score_bandCHAR(1)A | B | C | D | unrated — drives P(resume) in collections model
fleet_sizeINTNumber of vehicles in fleet (1 for individual)

payments

ColumnTypeDescription
payment_idUUID PKUnique payment record
contract_idUUID FK→ contracts.contract_id
period_year / period_monthINTBilling period (e.g. 2024, 3 = March 2024)
amount_dueNUMERIC(12,2)Invoice amount for the period
amount_paidNUMERIC(12,2)Actual cash received
payment_methodVARCHARdebit_order | eft | credit_card | cash
days_lateINTDays after due date that payment was received
statusVARCHARpaid | partial | unpaid | reversed | written_off

products

ColumnTypeDescription
product_idUUID PKUnique product identifier
product_tierVARCHARbasic | standard | premium | fleet
standard_monthly_feeNUMERIC(12,2)List price (actual contract fee may differ)
standard_cancellation_clauseINTDefault clause length in months
hardware_costNUMERIC(12,2)Upfront hardware cost for this product tier
monthly_platform_costNUMERIC(12,2)Per-contract monthly platform cost
country_codeCHAR(3)Country-specific product catalogue entry

costs_upfront & costs_monthly

Tablecost_type valuesDescription
costs_upfrontcac_marketing, cac_sales, cac_call_centre, fitment_labour, fitment_travel, hardwareOne-time acquisition and installation costs
costs_monthlyplatform, sim_data, support, repair, collections, retention_interventionRecurring monthly cost line items per billing period

hardware & fitment_records

ColumnTableDescription
residual_valuehardwareCurrent market value — used in EV(retrieve) calculation
statushardwareinstalled | retrieved | in_refurbishment | stock | lost | stolen
fitment_typefitment_recordsinstallation | retrieval | swap | repair
vehicle_complexityfitment_recordsstandard (1.0×) | complex (1.5×) | premium (1.8×) cost multiplier
total_costfitment_recordsActual technician cost for this visit

support_tickets

ColumnValuesNotes
categoryhardware_fault | billing | cancellation_request | service_query | recovery_requestcancellation_request → retentions workflow + penalty calc
resolutionresolved | escalated | pending | closed_no_actionSave rate = resolved / total cancellation_requests

recovery_events

ColumnTypeDescription
outcomeVARCHARrecovered | not_recovered | pending
recovery_fee_chargedNUMERIC(12,2)Invoice to subscriber (≈ 10× monthly fee)
recovery_fee_collectedNUMERIC(12,2)Cash collected — feeds recovery_pv in LTV
recovery_cost_to_cartrackNUMERIC(12,2)Operational cost of recovery operation

5. Analytical Models

5.1 LTV Engine — backend/analytics/ltv.py

Algorithm: Parametric Survival + DCF Horizon: 60 months Discount rate: 12% annual WACC → 0.949% monthly

Full forward-looking LTV formula:

WHERE
LTV Decomposition — Revenue components (subscription, penalty, recovery) minus cost components (upfront, monthly)
FIG 5 — LTV decomposition: revenue streams vs cost components with key drivers

Confidence interval: Monte Carlo (200 simulations). Samples monthly_churn_prob ~ N(μ, 0.25μ) and net_cashflow ~ N(μ, 0.15μ). Returns P10/P90 as the 80% CI.

Payback month: First month where cumulative discounted cashflow turns non-negative (upfront cost recovered).

5.2 Churn Classifier — backend/analytics/churn.py

Algorithm: XGBoost Classifier Features: 19 Validation AUC: 0.823
FeatureDescription
months_activeContract tenure in months
monthly_feeCurrent monthly subscription fee
credit_band_encEncoded credit band (A=0, B=1, C=2, D=3)
is_fleetBinary — fleet or corporate customer
avg_days_lateAverage payment lateness in days
pct_paidProportion of billing periods paid on time
consecutive_bad_monthsRatio of unpaid months to total tenure
ticket_countTotal support tickets raised
cancellation_ticketsCount of formal cancellation requests
country_zaf / ken / ngaCountry one-hot encodings (3 features)
channel_direct / dealer / onlineChannel one-hot encodings (3 features)
product_basic / premium / fleetProduct tier one-hot encodings (3 features)
vehicle_complexBinary — BMW, Mercedes, or Audi

Output conversion: Model outputs P(churn in 3 months). Converted to monthly:

Preservation tiers: RED > 60% 3-month churn probability · AMBER 30–60% · GREEN < 30%

Priority queue: Sorted by

5.3 Collections Decision Model — backend/analytics/collections.py

Algorithm: Expected Value Maximisation Decision frequency: Monthly per bad-billing contract

See Section 3 Bad Billing for formula details. Priority score for urgency ranking:

5.4 Pricing Engine — backend/analytics/pricing.py

Algorithm: SciPy minimize_scalar (bounded) Elasticity model: Exponential demand curve
OBJECTIVE
DEMAND MODEL
LTV AT PRICE
BREAK-EVEN
VERDICT

Segment elasticity parameters (α, β) indexed by (country, channel, tier). Example ZAF values:

CountryChannelTierα (base conversion)β (price sensitivity)
ZAFdirectstandard0.800.0018
ZAFdealerbasic0.900.0030
ZAFcorporatefleet0.950.0015
KENdirectbasic0.880.0040
NGAdirectbasic0.860.0045

6. API Reference

Base URL: http://localhost:8000/api/v1/ · Interactive docs: /docs

Management

MethodEndpointDescriptionKey Params
GET/management/portfolio-summaryTop-level C-suite KPIs (MRR, ARR, churn rate, gross profit)
GET/management/mrr-waterfall12-month MRR waterfall (new, churned, net)months=12
GET/management/unit-economics-by-cohortQuarterly cohort retention and payback
GET/management/cashflow-projection36-month forward cashflow with 80% CI bandshorizon_months=36
GET/management/churn-analyticsChurn rates by country, product tier, and tenure bandcountry (optional)
GET/management/contract/{id}Full contract drill-down with payment historycontract_id (path)

Sales

MethodEndpointDescriptionKey Params
POST/sales/pricing-calculatorReal-time optimal price + PROCEED/BLOCK verdictcountry_code, product_tier, channel, distance_km, vehicle_complexity
GET/sales/ltv-by-segmentLTV leaderboard by product, channel, country, or regiongroup_by, country
GET/sales/cohort-ltv-heatmapMonthly cohort cumulative revenue vs upfront cost
GET/sales/acquisition-funnelChannel CAC, retention rate, payback by channel
GET/sales/target-segmentsTop 20 segments ranked by avg_ltv × contract_count

Retentions

MethodEndpointDescriptionKey Params
GET/retentions/preservation-queueRanked list of at-risk contracts (RED/AMBER/GREEN)limit, tier, country
GET/retentions/churn-risk-distributionPortfolio churn score histogram + percentiles
GET/retentions/collections-queueBad billing EV decision queue with recommended actionslimit
GET/retentions/cancellation-noticesFormal cancellation request tickets + penalty at stake
GET/retentions/intervention-outcomesSave rate, resolved vs escalated cancellation requests

Governance & System

MethodEndpointDescription
GET/governance/model-registryRegistered model versions, AUC, drift status
GET/governance/churn-model-performanceCalibration by decile, AUC trend, precision/recall
GET/governance/feature-driftPSI per feature vs training baseline
GET/governance/data-qualityRecord counts, null rates, schema validity per source
GET/governance/audit-logPricing overrides, model events, drift alerts
GET/healthSystem health check

7. Dashboard Guide

DashboardURLAudienceKey Panels
Overview/All stakeholdersLive portfolio KPIs (6 cards), navigation to all dashboards
Sales & Pricing/salesSales agents, pricing teamPricing calculator → PROCEED/BLOCK, LTV by segment, acquisition funnel, cohort heatmap, target segments
Management/managementCEO, CFO, board7-card KPI strip, MRR waterfall, 36m cashflow projection, cohort unit economics, contract drill-down
Retentions/retentionsRetentions & collections agentsPreservation priority queue (filterable), churn risk histogram, intervention outcomes donut, collections EV queue
Governance/governanceData science, complianceModel registry, calibration chart, AUC trend, PSI drift table, data quality, audit log
Wiki/wikiAll teamsThis documentation page
All dashboards auto-refresh every 60 seconds. A live indicator (green dot) in the top-right confirms the connection is active.

8. Deployment

Prerequisites

Python 3.11+
pip install -r backend/requirements.txt

Start Server

# Option 1: direct
python -m uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload

# Option 2: Windows double-click
start.bat

# Option 3: Linux / Mac
bash start.sh

Access

Dashboards:   http://localhost:8000/
API docs:     http://localhost:8000/docs
Health:       http://localhost:8000/health

Fresh Seed

# Delete the database to force a fresh 5,000-contract seed on next startup
del cartrack_ltv.db        # Windows
rm cartrack_ltv.db         # Linux / Mac

# Quick test seed (200 contracts)
python -c "from backend.database import SessionLocal; from backend.data.seed import run_seed; db=SessionLocal(); run_seed(db, n_contracts=200)"

Environment Variables (.env)

VariableDefaultDescription
DATABASE_URLsqlite:///./cartrack_ltv.dbSQLAlchemy connection string. Use postgresql://... for production.
DISCOUNT_RATE_ANNUAL0.12WACC proxy for LTV discounting (12% annual)
ENVIRONMENTdevelopmentdevelopment | production
LOG_LEVELINFOPython logging level
SECRET_KEYdev-secret-keyMust be changed in production

Run Tests

# All tests (excludes slow XGBoost training by default)
python -m pytest tests/ -v

# Analytics only
python -m pytest tests/test_analytics_*.py -v

# API integration tests
python -m pytest tests/test_api_*.py -v

# With coverage
python -m pytest tests/ --cov=backend --cov-report=term-missing

High Availability (Production)

The platform is designed for single-node development but supports multi-node production deployment with the following HA configuration:

ComponentDev ModeHA ProductionHow to Enable
Web ServerUvicorn single workerGunicorn + N UvicornWorkers behind Nginx/ALBgunicorn backend.main:app -w 4 -k uvicorn.workers.UvicornWorker
DatabaseSQLite (file-based)PostgreSQL 15+ primary + read replicasSet DATABASE_URL=postgresql://user:pass@host/db
CacheIn-memory TTL dictRedis cluster (shared across workers)Set REDIS_URL=redis://host:6379/0
Task QueueSynchronousCelery + Redis brokerSet CELERY_BROKER_URL, run celery -A backend.tasks worker
Load BalancerNone (direct)Nginx / Traefik / AWS ALBTLS termination, sticky sessions for WebSocket
Connection PoolSQLAlchemy defaultPgBouncer for connection poolingPoint DATABASE_URL at PgBouncer, set pool_size=20
MonitoringConsole logsPrometheus + Grafana + SentryMount /metrics endpoint, configure Sentry DSN
ContainersBare metalDocker Compose / K8s / ECSdocker-compose up --scale app=4
ML ModelsIn-process trainingMLflow registry + scheduled retrainingSet MLFLOW_TRACKING_URI, Airflow/cron schedule
SecretsDefault dev keyVault / AWS Secrets ManagerSet SECRET_KEY to cryptographic random value
Critical for production: The SECRET_KEY must be changed from the default. The app will refuse to start in ENVIRONMENT=production with the dev key. Generate with: python -c "import secrets; print(secrets.token_hex(32))"

9. Model Governance

Model Registry

ModelAlgorithmFeaturesPrimary Metric
ChurnClassifierXGBoost (n=150, depth=4, lr=0.05)19AUC = 0.823
LTVSurvivalModelParametric survival (exponential)12MAE% = 8.3%
PricingEngineConstrained optimisation + elasticity9Margin delta = +2.1%
CollectionsDecisionModelExpected Value maximisation8Decision accuracy = 74%

PSI Thresholds (Population Stability Index)

PSI RangeStatusAction
< 0.10STABLENo action required
0.10 – 0.20MODERATEMonitor closely; investigate root cause
> 0.20ALERTTrigger model retraining; escalate to data science team

Audit Log Event Types

EventDescription
MODEL_RETRAINScheduled or drift-triggered retraining of a model
MODEL_DEPLOYPromotion of challenger to champion in production
PRICE_OVERRIDESales agent manually overrides engine-recommended price
DRIFT_ALERTPSI exceeded threshold on a monitored feature
DATA_INGESTIONNew data loaded into the platform
CONFIG_CHANGESystem configuration parameter modified

10. Glossary

TermDefinition
LTVLife Time Value — net present value of all future cashflows from a contract, discounted at WACC. Includes subscription PV, penalty PV, and recovery PV minus upfront cost.
MRRMonthly Recurring Revenue — sum of monthly_fee across all active contracts.
ARRAnnualised Recurring Revenue — MRR × 12.
Churn RateProportion of active contracts that cancel in a given month. Annualised = monthly × 12.
Bad BillingContract status where payment has lapsed but the contract has not yet been cancelled. Hardware remains installed. Decision pending (continue / cancel / retrieve).
Cancellation ClauseMinimum subscription term (typically 36 months). Early cancellation triggers a penalty = monthly_fee × remaining_months × 0.75.
Payback PeriodMonths until cumulative discounted net cashflow exceeds upfront cost. Payback = upfront_cost / monthly_net_margin.
PSIPopulation Stability Index — measures how much a feature's distribution has shifted between the training baseline and current production data.
WACCWeighted Average Cost of Capital — used as the discount rate proxy. Set at 12% annual (0.949% monthly) in this platform.
CACCustomer Acquisition Cost — upfront costs: marketing attribution + sales commission + call centre conversion cost.
Preservation QueueRanked list of active contracts ordered by LTV_at_risk × churn_probability. Used by retentions agents for proactive intervention.
Survival Curve — the probability that a contract remains active at month t.
Price ElasticityDemand model: . β measures price sensitivity; higher β = more price-sensitive segment.
Champion ModelThe model version currently deployed in production. Challenger models are evaluated before promotion.
EVExpected Value — probability-weighted outcome used in the bad billing decision model to compare continue / cancel / retrieve actions.
Save RateRetentions KPI = resolved cancellation requests / total cancellation requests. Measures the team's effectiveness at retaining subscribers who intend to cancel.