Skip to content

Forecasting Model

Scope: Business/domain documentation covering forecasting concepts, patient flow, and calculations. For technical architecture (Jotai state, React Query, component patterns), see ARCHITECTURE.md.

  1. Introduction & Model Overview
  2. Patient Flow Model
  3. Addressable Population Calculations
  4. Market Share Model
  5. Revenue/Sales Calculations
  6. User Workflow (7 Stages)
  7. Monte Carlo Simulation
  8. Data Inputs Reference

The Bioloupe forecasting model predicts pharmaceutical sales revenue by modeling the patient journey from disease incidence through treatment and calculating market share capture across multiple therapy lines.

flowchart TB
    A[Annual Incidence] --> B[Addressable Population]
    B --> C[Treatment Eligible Patients]
    C --> D[Market Share Applied]
    D --> E[New Patients per Therapy Line]
    E --> F[Revenue Calculation]
    F --> G[Total Sales Forecast]
ConceptDescription
IncidenceNew disease cases per year in a population
Addressable PopulationPatients who can potentially receive treatment
Therapy LineSequential treatment stage (1L, 2L, 3L+)
Peak ShareMaximum market share a product can achieve
Loss of Exclusivity (LoE)When patent protection ends and generics enter
Class SharePercentage of patients suitable for the therapy class (adjusts Peak Share)

This section provides intuitive explanations of the forecasting domain to help understand the “why” behind the formulas.

Why Addressable = Incidence + Relapse (Not Just Incidence)

Section titled “Why Addressable = Incidence + Relapse (Not Just Incidence)”

Incidence only counts NEW diagnoses in a given year. It does not include patients diagnosed in prior years.

Think of it like hospital admissions:

  • Incidence = New patients walking in the door THIS YEAR
  • Relapse = Prior patients coming BACK because their disease returned

Timeline Example (Breast Cancer):

YearEventWhere Counted
2023Patient diagnosed with EARLY stage cancer2023 Early Incidence
2024Patient completes early stage treatmentNot counted
2025Cancer returns and has now SPREAD2025 Early→Met Relapse

This 2025 relapse patient:

  • ❌ NOT in 2025 Metastatic Incidence (wasn’t newly diagnosed as metastatic)
  • ✅ IS in 2025 Early-to-Met Relapse count
  • ✅ DOES need metastatic treatment now

Who needs metastatic treatment in 2025?

Metastatic Addressable = New Met diagnoses (incidence) + Returning patients (relapse)
= 87,000 + 20,300 = 107,300 patients

Key Insight: Metastatic Addressable is ALWAYS ≥ Metastatic Incidence because relapse ADDS patients.

Why Multiply by Healthcare Access × Treatment Rate?

Section titled “Why Multiply by Healthcare Access × Treatment Rate?”

Not everyone with a disease gets treated. Real-world barriers reduce the population at each step:

100 patients with metastatic cancer
├─► Healthcare Access (95%)
│ - 5 patients can't access care (no insurance, remote location, unaware)
│ = 95 patients remaining
└─► Treatment Rate (80%)
- 19 patients don't get treated (refuse, too sick, contraindicated)
= 76 patients actually receive 1L treatment

Real-world reasons for each barrier:

BarrierExamples
Healthcare AccessNo insurance, lives far from hospital, undiagnosed, dies before treatment
Treatment RatePatient refuses treatment, doctor advises against it, comorbidities prevent treatment

The model handles two disease categories differently:

What they are: Cancers that form physical masses in specific organs (breast, lung, colon, prostate).

Why stage matters:

  • Early Stage = Tumor is localized, hasn’t spread, often curable with surgery
  • Metastatic = Cancer has spread to distant organs, requires systemic treatment
Example: Breast Cancer
├── 70% diagnosed Early Stage (localized to breast)
│ └── Some will relapse → adds to metastatic pool
└── 30% diagnosed Metastatic (already spread at diagnosis)

Treatment approaches differ drastically between stages, so the model tracks them separately.

What they are: Blood cancers (leukemia, lymphoma, multiple myeloma).

Why NO stage split:

  • Cancer is IN the blood or bone marrow
  • Already “systemic” from day one - no localized tumor
  • No meaningful “early vs metastatic” distinction
  • All patients flow directly to therapy lines
Solid Tumor: Incidence → [Early | Metastatic] → Relapse → Addressable → Therapy
Hematology: Incidence → Addressable → Therapy (no stage split)

Formula difference:

Solid Tumor Met Addressable = (Incidence × Met%) + (Incidence × Early% × Relapse%)
Hematology Addressable = Incidence × Healthcare Access%
┌─────────────────────────────────────────────────────────────────────┐
│ INCIDENCE: 290,000 new breast cancer cases/year (USA) │
└─────────────────────────────────────────────────────────────────────┘
┌───────────────┴───────────────┐
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────┐
│ EARLY STAGE │ │ METASTATIC STAGE │
│ 70% = 203,000 │ │ 30% = 87,000 │
│ (localized tumor) │ │ (spread to organs) │
└───────────────────────────┘ └───────────────────────────┘
│ ▲
│ RELAPSE (10% of early) │
└────────────────────────────────────┤
+20,300 patients │
┌───────────────────────────────┴───┐
│ METASTATIC ADDRESSABLE │
│ = 87,000 + 20,300 = 107,300 │
│ (incidence + relapse) │
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ Healthcare Access (95%) │
│ = 107,300 × 0.95 = 101,935 │
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ Treatment Rate (80%) │
│ = 101,935 × 0.80 = 81,548 │
│ ← 1L Met Treated Patients │
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ Market Share (35%) │
│ = 81,548 × 0.35 = 28,542 │
│ ← Patients on YOUR product │
└───────────────────────────────────┘

Common Confusion: “1L Met < Met Incidence - Is That Wrong?”

Section titled “Common Confusion: “1L Met < Met Incidence - Is That Wrong?””

It depends on what number you’re looking at:

MetricValueCan be < Incidence?
Met Addressable (before funnel)107,300❌ NO - always ≥ incidence
1L Met Treated (after funnel)81,548✅ YES - funnel reduces it
1L Met on Your Product (after share)28,542✅ YES - much smaller

The bug we fixed: The code was showing Addressable as lower than Incidence, which is mathematically impossible since Addressable = Incidence + Relapse.

The Configuration section has a left panel and a right panel.

Left panelSummaryCard: Shows incidence, stage distribution, and relapse rates at a glance.

Right panel — Varies by tree selection (TreeSelection: summary | line):

SelectionPanelContent
SummarySummaryPanelEditable incidence, stage mix, relapse rates
LineLinePanelLine-specific config (treatment rate, transition rate, custom variables, etc.)

Tree structure: Lines are listed directly within each stage section (Early/Met for solid tumors), with custom lines per-stage. There are no separate “Incidence” or “Stage” tree nodes — the summary view covers those.

Summary ← SummaryPanel (incidence + stage mix)
├── Early Stage
│ ├── Early 1L: 37,500 ← LinePanel (addressable count)
│ └── Early Custom Line ← LinePanel (editable name)
└── Metastatic
├── Met 1L: 80,500 ← LinePanel (addressable count)
└── Met 2L: 20,963 ← LinePanel (addressable count)
  • Reference lines have read-only names; only custom lines have editable names
  • Default lines show addressable count in the header; treatment eligible appears in a summary card at the end of the Patient Flow section

Key insight: Numbers go UP from Stage → 1L because relapse ADDS patients:

  • Met Stage: 70,000 (raw incidence × 70%)
  • Met 1L: 80,500 (70,000 + 10,500 relapse from early stage)

For 2L and beyond:

  • Only patients who ACCESSED previous treatment can progress
  • 2L Addressable = 1L Eligible × Transition Rate (not 1L Addressable)
  • This is clinically correct: you can’t progress from treatment you never received

2b. Biomarker Filtering (Funnel Multiplier)

Section titled “2b. Biomarker Filtering (Funnel Multiplier)”

Some cancer indications have biomarker-defined sub-populations — subsets of patients who test positive for a specific biomarker. For example, Oropharyngeal Cancer has an HPV Positive sub-population representing ~68% of cases.

When a biomarker filter is active, the model applies a funnel multiplier to the first-line addressable population. Biomarker does not modify incidence — it filters within the patient flow funnel.

biomarkerFactor = (biomarkerPercentage / 100) × (biomarkerTestingRate / 100) [when active]
= 1.0 [when inactive]

Applied in the funnel:

Addressable Population (incidence + relapse — unchanged by biomarker)
× biomarkerFactor ← applied here (first line only)
× Healthcare Access %
× Neo/Adj Factor (if applicable)
× Drug Treatment Rate
= Treatment Eligible
  • Biomarker Percentage: prevalence of the biomarker in the disease population (e.g., 68%)
  • Testing Rate: percentage of patients actually tested for this biomarker (e.g., 93%)

Example: HPV Positive, Oropharyngeal Cancer (USA)

Section titled “Example: HPV Positive, Oropharyngeal Cancer (USA)”
ParameterValue
Addressable Population15,004
HPV Positive Prevalence68%
HPV Testing Rate93%
biomarkerFactor0.68 × 0.93 = 0.6324
Filtered Addressable15,004 × 0.6324 = 9,489
  • Biomarker filtering is optional (toggle OFF by default)
  • Only one biomarker can be active per indication at a time
  • Applied to first lines only (Early 1L, Met 1L, Therapy 1L for hematology); subsequent lines inherit the filtered count via transition rates
  • Toggling OFF sets factor to 1.0 (no filtering)
  • Incidence count is never modified — biomarker is a derived multiplier via biomarkerFactorAtom

Biomarker UI appears in first-line panels (LinePanel → FirstLineExtras) under the “Biomarker” section, between relapse rates and patient flow fields.

  • Atoms: biomarkerFactorAtom, activeBiomarkerAtom, toggleBiomarkerAtom in atoms.ts
  • Computation: biomarkerFactor param on calculateLineFunnelForDisplay, calculateLinePatients, calculateLineSales, calculateTreatmentEligiblePatients
  • UI: FirstLineExtras component in LinePanel.tsx

2c. Neoadjuvant / Adjuvant Split (Early Stage Solid Tumors Only)

Section titled “2c. Neoadjuvant / Adjuvant Split (Early Stage Solid Tumors Only)”

For early-stage solid tumors, patients may receive treatment in different settings:

  • Neoadjuvant = treatment before surgery (shrink the tumor, then operate)
  • Adjuvant = treatment after surgery (operate, then treat to prevent recurrence)
  • Other = other treatment settings (e.g. maintenance, palliative)

The user selects one category and sets a single percentage that acts as a simple multiplier on the eligible population.

neoAdjFactor = neoAdjPercent / 100
  • When disabled or not applicable, factor = 1.0 (no effect)
  • The selected category (neoAdjSetting) is a label only — does not affect the math

Default: 100% = factor 1.0 (no reduction)

Addressable Population
× Healthcare Access
× Drug Treatment Rate
× [Setting] Split ← neoAdjPercent / 100
= Treatment Eligible

The neo/adj factor is applied after drug treatment rate. The toggle is per-line — any early-stage line can have it enabled independently. In sales/Monte Carlo calculations, the factor is applied to the first line only.

  • Toggle: User enables/disables the split per line
  • Segmented control: Pick one of 3 categories: Neoadjuvant, Adjuvant, or Other
  • Percentage input: Single percentage (0-100%) applied as a multiplier (default 100%)
  • Rollback: Resets percentage to 100%
  • Type: UnifiedLineData.neoAdjSetting (“neo” | “adj” | “other”), neoAdjPercent (0-100)
  • Formula: Simple multiplier — neoAdjPercent / 100 applied to eligible population
  • Calculation: getNeoAdjFactor() in computations.ts
  • UI: Segmented control + single percentage in LinePanel.tsx

2d. Custom Variables (Addressable Population Multipliers)

Section titled “2d. Custom Variables (Addressable Population Multipliers)”

Users can define up to 5 custom multiplier variables per therapy line (MAX_CUSTOM_VARIABLES = 5). Each custom variable represents a percentage applied multiplicatively to the addressable population, allowing users to model additional filtering factors not covered by the built-in parameters.

customMultiplier = Π(customVar.value / 100) — product of all custom variable percentages

Applied in the patient funnel (order matches code):

Addressable Population
× Healthcare Access %
× Neo/Adj Factor (if applicable — see section 2c)
× Drug Treatment Rate
× customMultiplier ← product of all custom variable percentages
= Treatment Eligible

First-line (1L) custom variables are synced to incidenceVars.customVariables for use in Monte Carlo and Tornado simulations. This ensures probabilistic analyses include the custom variable effects.

For 2L+ lines, the system uses findMatchingLineCustomVariable() to locate matching custom variables across lines, enabling consistent filtering across the therapy cascade.

  • Computation: getCustomMultiplier() in computations.ts
  • Matching: findMatchingLineCustomVariable() in computations.ts
  • Constant: MAX_CUSTOM_VARIABLES in constants.ts

The model tracks patients through a funnel from initial diagnosis to treatment across multiple therapy lines.

flowchart TD
    subgraph Incidence["Disease Incidence"]
        I[Base Incidence] --> PG[Population Growth Adjustment]
        PG --> RF[Regional Factor Adjustment]
    end

    subgraph StageSplit["Stage Split (Solid Tumors Only)"]
        RF --> |Solid Tumor| ES[Early Stage %]
        RF --> |Solid Tumor| MS[Metastatic %]
        RF --> |Hematology| TH[Full Incidence]
        ES --> E2E[+ Early to Early Relapse %]
        ES --> E2M[Early to Met Relapse %]
        E2M --> MS
    end

    subgraph Addressable["Addressable Population (1L)"]
        E2E --> BF1["× Biomarker Factor"]
        BF1 --> EA["Early Addressable × HC%"]
        MS --> BF2["× Biomarker Factor"]
        BF2 --> MA["Met Addressable × HC%"]
        TH --> BF3["× Biomarker Factor"]
        BF3 --> TA["Therapy Addressable × HC%"]
    end

The model handles two main disease categories differently:

CategoryStage SplitExample Indications
Solid TumorsYes - Early Stage + MetastaticBreast Cancer, Lung Cancer, Melanoma
HematologyNo - Full incidence flows to therapyLeukemia, Lymphoma, Multiple Myeloma
flowchart TD
    A[1L Addressable Population] --> |Treatment Rate %| B[1L Eligible Patients]
    B --> |Market Share %| C[1L New Patients on Product]

    B --> |Pool minus Retreatment| D[Available for 2L]
    D --> |Transition Rate %| E[2L Eligible Patients]
    E --> |Treatment Rate %| E2[2L Treated]
    E2 --> |Market Share %| F[2L New Patients on Product]

    E2 --> |Pool minus Retreatment| G[Available for 3L]
    G --> |Transition Rate %| H[3L Eligible Patients]
    H --> |Market Share %| I[3L New Patients on Product]

Key Transition Parameters:

  • Drug Treatment Rate: Percentage of eligible patients who receive any treatment (applied to 1L and 2L only)
  • Transition Rate: Percentage of patients who move from one therapy line to the next
  • Retreatment Factor: Patients staying on current therapy (reduces pool for next line)
  • Re-treatment Toggle: Binary on/off setting that enables/disables the retreatment calculation for a line

Important: Drug Treatment Rate is applied to 1L and 2L lines. Lines 3L and beyond do not apply Drug Treatment Rate - all transitioned patients are considered eligible.


The base incidence grows with population and adjusts for regional factors:

Year Incidence = Base Incidence × (Pop[Year] / Pop[BaseYear]) × Regional Factor

Example:

  • Base Incidence (2024): 50,000 patients
  • Population 2024: 330 million
  • Population 2030: 345 million
  • Regional Factor (USA): 1.0
Incidence 2030 = 50,000 × (345M / 330M) × 1.0 = 52,273 patients

Addressable Population by Disease Category

Section titled “Addressable Population by Disease Category”

Solid tumors split into Early Stage and Metastatic populations with relapse transitions:

Early Addressable:

Early Addressable = Incidence × EarlyStage% × (1 + EarlyToEarlyRelapse%) × HealthcareAccess%

Metastatic Addressable:

Met Addressable = (Incidence × MetStage% + Incidence × EarlyStage% × EarlyToMetRelapse%) × HealthcareAccess%

Example (Breast Cancer):

  • Incidence: 50,000
  • Early Stage: 70%
  • Metastatic: 30%
  • Early to Early Relapse: 15%
  • Early to Met Relapse: 10%
  • Healthcare Access: 95%
Early Addressable = 50,000 × 0.70 × (1 + 0.15) × 0.95 = 38,238 patients
Met Addressable = (50,000 × 0.30 + 50,000 × 0.70 × 0.10) × 0.95 = 17,575 patients

Hematology indications do not have stage splits:

Addressable = Incidence × HealthcareAccess%
ParameterDescriptionTypical Range
Early Stage %Initial early-stage diagnosis60-80%
↳ Localized %Sub-breakdown: localized early stage (optional)Varies
↳ Locally Advanced %Sub-breakdown: locally advanced III-IVB (optional)Varies
Metastatic %Initial metastatic diagnosis (derived: 100 - Early%)20-40%
Unknown Stage %Patients with unknown stage at diagnosis (optional)0-100%
Early to Early Relapse %Early (Local + Locally Advanced) patients who relapse but stay early5-20%
Early to Met Relapse %Early (Local + Locally Advanced) patients who progress to metastatic5-15%

Note: Early Stage % + Metastatic % + Unknown Stage % = 100% (Unknown defaults to 0).

Early Stage Sub-Variables (Solid Tumors Only)

Section titled “Early Stage Sub-Variables (Solid Tumors Only)”

Some solid tumors have granular staging data that breaks Early Stage into Localized and Locally Advanced (III-IVB) sub-populations. These sub-variables are display-only — the computation layer always uses the combined earlyStagePercent.

Behavior matrix:

Data AvailableParent (Early Stage %)LocalizedLocally AdvancedExample
Both subsRead-only (auto-sum)EditableEditableOropharyngeal: 15.8 + 72.9 = 88.7%
Parent + one subEditableRead-onlyRead-onlyPartial reference data
Parent onlyEditableHiddenHiddenMost indications (today’s behavior)
No dataFallback chainHiddenHiddenMissing geo-specific data

Auto-computation: When both sub-variables are present, the parent is derived:

earlyStagePercent = earlyStageLocalizedPercent + earlyStageLocallyAdvancedPercent
metStagePercent = 100 - earlyStagePercent

Reset behavior: Resetting a sub-variable also resets the parent and metastatic percentage to maintain consistency.

GeographyHealthcare Access
USA95%
EU598%
Japan99%
China95%

Peak Share is the maximum market share a product can achieve, determined by competitive positioning:

flowchart LR
    subgraph Inputs["Peak Share Inputs"]
        LO[Launch Order<br/>1st to 10th]
        BIC[Best-in-Class?<br/>Yes/No]
        DVC[Delay vs Competition<br/>Quarters]
    end

    subgraph Calculation["Peak Share Calculation"]
        LO --> BASE[Base Share from Matrix]
        BIC --> BONUS[Best-in-Class Bonus]
        DVC --> PENALTY[Delay Penalty]
        BASE --> PS[Peak Share %]
        BONUS --> PS
        PENALTY --> PS
    end

Formulas:

Peak Share (within class):

PeakShare = min(100, max(0, BaseShare[LaunchOrder] + BestInClassBonus - DelayPenalty))

Effective Peak Share (used in market share calculations):

EffectivePeakShare = CustomEffectivePeakShare (if user has toggled Custom Input)
OR PeakShare × (ClassShare / 100) (Bioloupe Guidance — default)

Where:

  • BaseShare comes from a 10x10 launch order matrix (indexed by launch order and number of competitors)
  • BestInClassBonus is added if the product is best-in-class (indexed by number of competitors)
  • DelayPenalty = DelayQuarters > 3 ? DelayQuarters × 0.5 : 0
  • ClassShare is the percentage of patients suitable for the therapy class (default 100%)

Example:

  • Launch Order: 2nd to market (with 3 competitors)
  • Best in Class: Yes
  • Delay: 6 quarters behind first entrant
  • Base Share (2nd position, 3 competitors): 29%
  • Best-in-Class Bonus (3 competitors): +30%
  • Delay Penalty: 6 × 0.5 = 3%
Peak Share = 29% + 30% - 3% = 56%

Key Insight:

  • Peak Share is the CEILING - the maximum market share your product can ever achieve
  • Uptake Curve determines HOW FAST you reach that ceiling
flowchart TB
    subgraph "Peak Share = Maximum Market Capture"
        INPUT1["Launch Order<br/>(Earlier = Better)"]
        INPUT2["Best in Class<br/>(Better Drug = Bonus)"]
        INPUT3["Delay vs Competition<br/>(Late = Penalty)"]

        INPUT1 --> CALC["Calculate Peak Share"]
        INPUT2 --> CALC
        INPUT3 --> CALC

        CALC --> OUTPUT["Peak Share %<br/>(Your ceiling)"]
    end

Think of it like a race:

  • Peak Share = How far you can ultimately run (your maximum distance)
  • Uptake Curve = How quickly you accelerate to your top speed

A product with 60% peak share using a “3 Year Fast” uptake curve will reach 60% × 60% = 36% market share in Year 1, then 60% × 85% = 51% in Year 2, and finally the full 60% in Year 3.

Products don’t achieve peak share immediately. The uptake curve determines how quickly market share ramps up.

The system provides 30+ uptake curves organized by years to peak (1-12) and speed (Slow/Medium/Fast).

Example: 3-Year Curves

Curve NameYear 1Year 2Year 3+
3 Year Slow10%50%100%
3 Year Medium26%66%100%
3 Year Fast60%85%100%

Note: Curves are named like “3 Year Medium”, “5 Year Fast”, etc. The number indicates years to reach 100% of peak share.

flowchart LR
    subgraph PreLaunch["Pre-Launch"]
        PL[Market Share = 0%]
    end

    subgraph Ramp["Ramp Period"]
        PS[Peak Share] --> UC[Uptake Curve Applied]
        UC --> MS1[Growing Share]
    end

    subgraph Mature["Mature Period"]
        MS1 --> PEAK[At Peak Share]
    end

    subgraph PostLoE["Post-LoE"]
        PEAK --> ER[Erosion Curve]
        ER --> FINAL[Declining Share]
    end

    PreLaunch --> Ramp
    Ramp --> Mature
    Mature --> PostLoE

Year N Market Share Formula:

MarketShare[Year] = UptakeCurve[YearOffset] × EffectivePeakShare × (1 - ErosionRate[YearsPostLoE])

Where EffectivePeakShare = CustomEffectivePeakShare (if set) or PeakShare × (ClassShare / 100)

First Year Weighting (for mid-year launches):

WeightedShare = (Uptake[Y1] × (13 - LaunchMonth) + Uptake[Y0] × (LaunchMonth - 1)) / 12

After LoE, market share erodes due to generic/biosimilar competition:

Molecule TypeYear 1Year 2Year 3
Small Molecule60%80%84%
Biologic3%30%70%

Note: Small molecules face rapid generic erosion; biologics erode more slowly due to biosimilar complexity.


flowchart LR
    NP[New Patients] --> CALC((×))
    PRICE[Net Price per Month] --> CALC
    CALC --> CALC2((×))
    COMP[Compliance %] --> CALC2
    CALC2 --> CALC3((×))
    MOT[Months of Therapy] --> CALC3
    CALC3 --> DIV[÷ 1,000,000]
    DIV --> SALES[Line Sales $M]

Price typically changes annually from the launch price:

NetPrice[Year] = LaunchPrice × (1 + AnnualPriceChange%)^(Year - LaunchYear)

Example:

  • Launch Price: $15,000/month
  • Annual Price Change: -2%
  • Year of Launch: 2025
YearCalculationNet Price
2025$15,000 × (0.98)^0$15,000
2026$15,000 × (0.98)^1$14,700
2027$15,000 × (0.98)^2$14,406
2030$15,000 × (0.98)^5$13,537
LineSales ($M) = Σ(CohortPatients[yearOffset] × NetPrice × Compliance% × MonthsThisYear[yearOffset]) / 1,000,000

MonthsOfTherapy is distributed across calendar years using cohort logic — each cohort year contributes up to 12 months. For example, a 24-month therapy generates 12 months of revenue in year 0 (from that year’s new patients) and 12 months in year 1 (from the prior year’s cohort). When MonthsOfTherapy ≤ 12, only one cohort year contributes and the formula reduces to NewPatients × NetPrice × Compliance% × MonthsOfTherapy / 1M.

Example (1L Therapy):

  • New Patients: 5,000
  • Net Price: $12,000/month
  • Compliance: 85%
  • Months of Therapy: 10
LineSales = 5,000 × $12,000 × 0.85 × 10 / 1,000,000 = $510M
TotalSales = Sum of LineSales across all therapy lines (1L + 2L + 3L...)

Scenario: Oncology product, 2nd line therapy, Year 3 post-launch

InputValue
Addressable Population30,000
Treatment Rate80%
Transition Rate (from 1L)60%
Market Share (Year 3)35%
Net Price$14,000/month
Compliance90%
Months of Therapy8

Calculation:

1. Eligible Pool = 30,000 - 1L_Patients retained
(Assume 15,000 available after 1L retention)
2. 2L Eligible = 15,000 × 60% × 80% = 7,200 patients
3. New Patients = 7,200 × 35% = 2,520 patients
4. Sales = 2,520 × $14,000 × 0.90 × 8 / 1,000,000 = $254M

The application guides users through seven sequential stages to build their forecast:

flowchart LR
    S1[1. Incidence Info] --> S2[2. Landscape]
    S2 --> S3[3. Key Assumptions]
    S3 --> S4[4. Model]
    S4 --> S5[5. Sales Chart]
    S4 --> S6[6. Monte Carlo]
    S4 --> S7[7. Custom Data]

    S1 -.-> |"Geography<br/>Indication"| S2
    S2 -.-> |"Patient Populations<br/>Line Definitions"| S3
    S3 -.-> |"Market & Price<br/>Parameters"| S4
    S4 -.-> |"Calculated Sales"| S5
    S4 -.-> |"Variable Ranges"| S6
    S7 -.-> |"Override Curves"| S4

Purpose: Define the disease context and patient population

User Inputs:

  • Geography selection (USA, EU5, Japan, China)
  • Indication/disease selection
  • Base incidence numbers
  • Stage mix parameters (for solid tumors)

Outputs: Base patient population for modeling

Purpose: Define the competitive landscape and therapy lines

User Inputs:

  • Therapy lines (1L, 2L, 3L, etc.)
  • Products in each line
  • Launch dates
  • Categories (early, metastatic, therapy)

Outputs: Market structure for competitive analysis

Purpose: Set market and pricing parameters

User Inputs:

  • Loss of Exclusivity (LoE) date
  • Molecule type (biologic vs small molecule)
  • Launch price per month
  • Annual price change
  • Peak share inputs (launch order, best-in-class, delay)
  • Speed to peak selection

Outputs: Parameters for market share and revenue calculations

Purpose: View calculated forecasts by therapy line

Displays:

  • Year-by-year patient numbers
  • Market share progression
  • Sales by therapy line
  • Total sales summary

Interactivity: Accordion views for each therapy line with detailed metrics

Purpose: Visualize sales forecast

Displays:

  • Stacked area chart of sales by therapy line
  • Year-over-year trends
  • Peak sales identification

Purpose: Analyze forecast uncertainty

User Inputs:

  • Variable ranges (min, max, most likely)
  • Distribution types
  • Number of simulations

Outputs:

  • Percentile forecasts (P10, P50, P90)
  • Tornado chart showing variable sensitivity

Purpose: Fine-tune projection curves for model accuracy

User Inputs:

  • Net Price Change: Year-over-year price changes (when annual price is changeable)
  • Share Erosion: Post-LoE share erosion curves (Small Molecule or Biologics, when molecule type is changeable)
  • Incidence Evolution: Projected growth rates (%) that cascade-recalculate incidence values

Incidence Evolution Feature:

  • 3-row display: Year | Projected Growth Rate (%) | Incidence
  • Editing a growth rate at year N cascades updates to all subsequent years
  • Growth rate is constant across all years (acceleration is baked into the API-provided rate)
  • Formula: incidence[N+1] = incidence[N] × (1 + growthRate / 100)
  • Incidence values are read-only (auto-calculated from growth rates)
  • First year shows ”-” for growth rate (no prior year to compare)

Outputs: Customized evolution curves that feed back into model calculations


Monte Carlo simulation quantifies forecast uncertainty by running 1,000 to 100,000 scenarios with randomly sampled variable values. The simulation count is configurable via a badge in the section header; counts above 1,000 run in a Web Worker to keep the UI responsive.

flowchart TD
    subgraph Inputs["Variable Definitions"]
        V1["Variable 1<br/>Min: 10 | Mode: 15 | Max: 25"]
        V2["Variable 2<br/>Min: 50 | Mode: 70 | Max: 80"]
        VN["Variable N<br/>Min: 5 | Mode: 8 | Max: 12"]
    end

    subgraph Simulation["Monte Carlo Engine"]
        V1 --> SAMPLE[Sample from Distributions]
        V2 --> SAMPLE
        VN --> SAMPLE
        SAMPLE --> |"1K–100K iterations"| MODEL[Run Full Sales Model]
        MODEL --> COLLECT[Collect Results]
    end

    subgraph Outputs["Analysis Outputs"]
        COLLECT --> DIST[Distribution of Outcomes]
        COLLECT --> PERC["Percentiles<br/>P10 / P50 / P90"]
        COLLECT --> TORNADO[Tornado Chart]
    end

Variables that can be simulated include:

CategoryVariables
IncidenceBase incidence, Stage mix percentages, Unknown stage percentage, Relapse rates
TreatmentTreatment rate, Healthcare access
MarketPeak share, Months of therapy
PricingLaunch price
CustomCustom multiplier variables (up to 5 per line)

Note: Transition rate, Compliance, and Annual price change are not currently simulated in Monte Carlo analysis.

Incidence Evolution: Monte Carlo and Tornado analyses use the evolved incidence values from Custom Inputs (Stage 7). For each simulated year, getYearIncidence(year) is called to apply the growth rates defined in the Incidence Evolution table, ensuring projections reflect year-over-year incidence changes.

DistributionUse CaseParameters
TriangularMost common - when you have min/max/modeMin, Mode, Max
NormalSymmetric uncertaintyMean, Std Dev
UniformEqual probability across rangeMin, Max

The tornado chart ranks variables by their impact on forecast uncertainty:

For each variable:
1. Hold all other variables at most likely values
2. Run model at variable's minimum value -> Low result
3. Run model at variable's maximum value -> High result
4. Impact = |High - Low|
5. Rank variables by impact (largest at top)

Interpretation:

  • Variables at the top of the tornado have the greatest influence on uncertainty
  • Focus validation efforts on high-impact variables
  • Consider strategies to reduce uncertainty in key drivers
PercentileMeaning
P1010% chance results will be lower (pessimistic)
P50Median outcome (50% above, 50% below)
P9090% chance results will be lower (optimistic)

Each indication requires configuration of:

Disease Config
├── name: "Non-Small Cell Lung Cancer"
├── type: "Solid Tumor" | "Hematology"
├── abbreviation: "NSCLC"
├── incidence
│ ├── base: 230000
│ └── stageMix (Solid Tumors only)
│ ├── earlyStagePercent: 30
│ ├── earlyStageLocalizedPercent?: 15.8 (optional sub-breakdown)
│ ├── earlyStageLocallyAdvancedPercent?: 72.9 (optional sub-breakdown)
│ ├── metStagePercent: 70
│ ├── unknownStagePercent?: 0 (optional, defaults to 0)
│ ├── earlyToEarlyRelapse: 10
│ └── earlyToMetRelapse: 15
└── retreatment: 0.8

The initialization flow is geo-aware: each geography derives its own baseIncidence, growthRate, healthcareAccess, stage mix, and treatment line configuration from the API data. When geo-specific data is unavailable, the fallback chain is: Requested geo → USA → EU5 → Japan → China → field-level defaults.

This means diseases with geo-specific incidenceGrowth values in the API will produce different incidence evolution projections per geography, rather than applying USA values to all geos.

GeographyRegional FactorHealthcare Access
USA1.0 (baseline)95%
EU5Varies by indication98%
JapanVaries by indication99%
ChinaVaries by indication95%
ParameterDefault
Early Stage %100%
Metastatic %0%
Early to Early Relapse %0%
Early to Met Relapse %0%
Year Post-LoESmall MoleculeBiologic
Year 160%3%
Year 280%30%
Year 384%70%
ParameterDefault
Drug Treatment Rate100%
Transition Rate100%
Compliance100%
Months of Therapy12
  • Data array length: 20 years (maximum projection horizon)
  • Default visible range: 10-11 years (configurable in UI)
  • Start year: Current year or user-selected
  • Data points: Annual values

YearIncidence = BaseIncidence × (Pop[Year]/Pop[Base]) × RegionalFactor
EarlyAddressable = Incidence × EarlyStage% × (1 + E2E%) × HC%
MetAddressable = (Incidence × MetStage% + Incidence × EarlyStage% × E2M%) × HC%
Addressable = Incidence × HC%
PeakShare = BaseShare[LaunchOrder, Competitors] + BestInClassBonus[Competitors] - DelayPenalty
DelayPenalty = Delay > 3 ? Delay × 0.5 : 0
EffectivePeakShare = CustomEffectivePeakShare OR PeakShare × (ClassShare / 100)
MarketShare = Uptake[Year] × EffectivePeakShare × (1 - Erosion[YearsPostLoE])

When stage distribution includes an “Unknown” category, the unknown portion is redistributed proportionally across known stages (Option A: Proportional Redistribution):

knownTotal = earlyStagePercent + metStagePercent
effectiveEarlyPct = earlyStagePercent / knownTotal
effectiveMetPct = metStagePercent / knownTotal
earlyIncidence = totalIncidence × effectiveEarlyPct
metIncidence = totalIncidence × effectiveMetPct

Example: Incidence=154,270, Early=71%, Met=23%, Unknown=6%

knownTotal = 71 + 23 = 94
effectiveEarly = 71/94 = 75.53% → 116,523 patients
effectiveMet = 23/94 = 24.47% → 37,747 patients
Total accounted: 154,270 (100%)

When unknownStagePercent = 0 (default), knownTotal = 100% and the formula reduces to the identity — no change from direct percentage usage.

Unknown is logically a child of Early Stage. The UI constraint earlyStagePercent + metStagePercent + unknownStagePercent = 100% is enforced as:

  • When Unknown changes: earlyStagePercent = max(0, 100 - metStagePercent - unknownStagePercent) (Early absorbs the change; Met stays fixed). Sub-variables (Localized, Locally Advanced) scale proportionally.
  • When Early changes: metStagePercent = max(0, 100 - earlyStagePercent - unknownStagePercent) (Met absorbs the change; Unknown stays fixed).
neoAdjFactor = neoAdjPercent / 100
= 1.0 when disabled or 100%
1L: Eligible = Addressable × HC% × NeoAdjFactor × TreatmentRate%
NewPatients = Eligible × MarketShare%
2L: Pool = 1L_Eligible - (1L_NewPatients × Retreatment)
Eligible = Pool × TransitionRate% × TreatmentRate%
NewPatients = Eligible × MarketShare%
3L+: Pool = PriorEligible - (PriorNewPatients × Retreatment)
Eligible = Pool × TransitionRate% (NO TreatmentRate applied)
NewPatients = Eligible × MarketShare%

Notes:

  • Treatment Rate only applies to 1L and 2L. Lines 3L and beyond do not apply an additional Treatment Rate filter.
  • Neo/Adj Factor only applies to the first line of an early-stage solid tumor cascade. When disabled, factor = 1.0.
NetPrice[Year] = LaunchPrice × (1 + AnnualChange%)^(Year - LaunchYear)
LineSales = Σ(CohortPatients[yearOffset] × NetPrice × Compliance% × MonthsThisYear[yearOffset]) / 1,000,000
TotalSales = Sum(LineSales) for all lines