
Margin Simulation
- 1 installs
- 1 repo stars
- Updated May 7, 2026
- afelipeg/anthropic-skills-for-enterprise-marketing-os
margin-simulation is a Claude Code skill that models agency margin, P&L and EBITDA using Monte Carlo simulation and leakage detection to test pricing and profitability.
About
margin-simulation is a Claude Code skill that models the commercial viability of an agency engagement. It builds a deterministic P&L, runs a Monte Carlo simulation for uncertainty, detects margin leakage, and runs sensitivity and scenario analysis. A developer or agency operator uses it to answer whether a fee, retainer or campaign is profitable and what to charge. It ships a Python engine (margin_engine.py) and a pricing and leakage reference.
- Models agency margin, P&L and EBITDA per client or scope
- Runs Monte Carlo simulation (5,000 draws) for uncertainty
- Detects margin leakage across 8 patterns and 3 severity tiers
Margin Simulation by the numbers
- 1 all-time installs (skills.sh)
- Ranked #909 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Jul 7, 2026 (Skillselion catalog sync)
margin-simulation capabilities & compatibility
Runs locally with a bundled Python engine; numpy preferred but not required.
- Capabilities
- margin modeling · monte carlo simulation · pricing analysis · scenario planning
- Use cases
- data analysis
- Pricing
- Free
What margin-simulation says it does
Monte Carlo simulation (5,000 draws)
margin, delivery cost, cost-to-serve, leakage, FTE economics
npx skills add https://github.com/afelipeg/anthropic-skills-for-enterprise-marketing-os --skill margin-simulationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 1 |
| Last updated | May 7, 2026 |
| Repository | afelipeg/anthropic-skills-for-enterprise-marketing-os ↗ |
What it does
Model agency margin, P&L and pricing sufficiency with Monte Carlo simulation and leakage detection.
Who is it for?
Agency operators validating whether a fee, retainer or campaign is profitable and what to charge
Skip if: Product-level unit economics, trading, or non-agency financial modeling
When should I use this skill?
User asks about margin, P&L, EBITDA, pricing, fee sufficiency or whether an engagement is profitable
What you get
A margin percentage, Monte Carlo distribution, leakage waterfall and scenario cards to guide pricing
- margin dashboard
- monte carlo distribution
- leakage waterfall
By the numbers
- Monte Carlo with 5,000 draws
- 8 leakage patterns across 3 severity tiers
- 8-step simulation process
Files
Margin Simulation
Model the full commercial viability of any agency engagement — from a single campaign to a multi-year retainer — using deterministic P&L analysis, Monte Carlo simulation for uncertainty quantification, leakage detection, sensitivity analysis, and scenario modeling.
How This Skill Thinks
This skill does not just calculate margin. It orchestrates a pipeline:
1. Script execution (scripts/margin_engine.py): Runs the deterministic P&L, Monte Carlo simulation (5,000 draws), leakage detection, and sensitivity analysis programmatically 2. Reference lookup (references/leakage_and_pricing.md): Provides benchmark data, leakage taxonomy, pricing strategies, and the 70/30 model impact on margin 3. Qualitative judgment (Claude): Interprets results in context — client relationship dynamics, market positioning, competitive pressure, historical patterns — and shapes the recommendation 4. Visual output (Visualizer): Renders the margin dashboard as an inline widget with Monte Carlo distribution, leakage waterfall, and scenario cards
The script produces numbers. Claude produces insight. The Visualizer makes it actionable. None of these replaces the other.
Quick Reference
| Resource | Purpose | Usage |
|---|---|---|
scripts/margin_engine.py | Core simulation engine — deterministic P&L + Monte Carlo (5K runs) + leakage detection + sensitivity analysis + scenario modeling | python margin_engine.py --input config.json --output margin.json |
references/leakage_and_pricing.md | Leakage taxonomy (8 patterns, 3 severity tiers), margin benchmarks by engagement type, Monte Carlo methodology, pricing strategies, 70/30 impact analysis | Read for benchmarks and methodology explanation |
How to Use the Script
Build a config JSON from the user's inputs, then run the engine:
import sys
sys.path.insert(0, "<skill-path>/scripts")
from margin_engine import MarginSimulator
config = {
"client_name": "FreshBrew Coffee",
"engagement_type": "retainer",
"monthly_revenue": 15000,
"contract_months": 12,
"margin_target": 0.30,
"fte_costs": [
{"name": "Creative (mid)", "monthly_amount": 1250, "seniority": "mid", "fte": 0.5},
{"name": "Account (mid)", "monthly_amount": 560, "seniority": "mid", "fte": 0.2},
{"name": "Data (mid)", "monthly_amount": 450, "seniority": "mid", "fte": 0.15},
],
"vendor_costs": [],
"tool_costs": [{"name": "Design tools", "monthly_amount": 150}],
"ai_infrastructure": 500,
# Leakage flags — True means protection exists
"has_revision_cap": True,
"has_pm_hours": False,
"has_change_order": True,
"has_client_sla": True,
"has_vendor_caps": True,
"has_seniority_match": True,
"has_adhoc_cap": True,
"has_tool_passthrough": False,
}
sim = MarginSimulator(config)
result = sim.run()
# result.margin_pct, result.monte_carlo, result.leakage_items, result.scenarios, etc.If the user has already run fte-capacity-sizing, use those FTE costs directly — the capacity model's output feeds this simulation's input.
Calibration note: Monte Carlo results depend on uncertainty parameters. Default uncertainty is ±10% on FTE costs, ±15% on vendor costs. If the user provides actual variance data or historical overrun rates, use those instead. The simulation uses N=5,000 draws by default; this produces stable percentiles (±0.5pp). Numpy is preferred but the engine falls back to pure Python if unavailable.
Trigger Conditions
Activate this skill when:
- The user asks about margin, profitability, P&L, or EBITDA for a client or scope
- The user wants to know if a fee can support the delivery cost
- The user asks "what should we charge?" or "is this fee enough?"
- The
fte-capacity-sizingproduces a cost model that needs financial validation - The
scope-auditidentifies commercial risk and routes here - The
agency-request-intake-routerflags a financial concern - The user is building a proposal and needs pricing validation
Simulation Process
Work through all eight steps. The script handles Steps 2-7 computationally; you handle Steps 1 and 8 (context gathering and recommendation framing).
Step 1 — Capture Revenue and Cost Inputs
Gather from the user or from upstream skills:
- Revenue: Monthly fee, retainer amount, or project fee (÷ months)
- FTE costs: From
fte-capacity-sizingoutput or direct input (role × rate × FTE) - Vendor costs: Production, media ops, freelancers, specialized services
- Tool costs: SaaS subscriptions, platform licenses, API costs
- AI infrastructure: Claude API, MCP hosting, agent pipeline costs (default: $500/mo)
- Contract term: Duration in months (default: 12)
- Margin target: Default 30%, adjustable by user
If any cost category is missing, flag it as ⚠️ and use benchmarks from references/leakage_and_pricing.md.
Step 2 — Build Deterministic P&L
Calculate the base-case margin:
Total delivery cost = FTE cost + Vendor cost + Tool cost + AI infra
+ Overhead (12% of FTE + Vendor)
+ Rework buffer (10% of FTE cost)
+ Risk buffer (5% of FTE + Vendor)
Gross margin = Revenue - Total delivery cost
Margin % = Gross margin / Revenue × 100
OI / EBITDA impact = Gross margin × Contract monthsStep 3 — Detect Leakage
Check the scope for each of 8 leakage patterns. For each unprotected source, calculate the monthly cost impact and the adjusted margin. See references/leakage_and_pricing.md → "Leakage Taxonomy" for the full pattern catalog.
Step 4 — Run Monte Carlo Simulation
Execute 5,000 Monte Carlo draws to model margin uncertainty. Each draw applies random perturbation to costs, plus stochastic scope creep and client delay events. The output is a margin distribution with P10/Mean/P90 range and probability of falling below target.
Read references/leakage_and_pricing.md → "Monte Carlo Methodology" for the full algorithm specification.
Step 5 — Run Sensitivity Analysis
Test each cost driver with a ±20% shock to identify which driver has the largest marginal impact on margin. This answers: "Where should I negotiate?" — focus energy on the most sensitive driver.
Step 6 — Build Scenarios
Generate 4 scenarios:
- Base case: Deterministic P&L as calculated
- Optimistic: 15% cost reduction (AI uplift, no scope creep)
- Pessimistic: 30% cost increase (scope creep + rework + delays)
- With leakage: Base cost + all detected leakage impacts
Step 7 — Determine Verdict
Issue a commercial verdict based on margin level, Monte Carlo risk, and leakage exposure:
| Condition | Verdict |
|---|---|
| Margin ≥ 40% | Highly viable |
| Margin 30-40% (target met) | Viable |
| Margin 15-30% | Tight — vulnerable to creep |
| Margin 0-15% | Underfunded — near breakeven |
| Margin < 0% | Non-viable — losing money |
Monte Carlo enrichment: if P(below target) > 50%, flag structural risk regardless of base margin.
Step 8 — Frame the Recommendation
This is your qualitative layer — the script produces numbers, you produce judgment:
- What should the user do about it? (Renegotiate? Reduce scope? Increase AI automation?)
- What's the client context? (New relationship worth investing in? Legacy client with history of creep?)
- What are the alternatives? (Walk away? Restructure as project-based? Phase the work?)
Output Format
Produce the margin simulation in TWO forms: first as an inline visual artifact (rendered in chat via the Visualizer), then as a structured markdown report below it.
Visual Artifact (Primary)
Render the margin dashboard as an inline HTML widget using the Visualizer. The widget should display:
- A header bar color-coded by verdict: dark green (Highly viable), green (Viable), amber (Tight), orange (Underfunded), red (Non-viable)
- A top metrics row with 4 cards: Monthly revenue, Total cost, Margin %, OI/EBITDA
- A cost waterfall — stacked horizontal bar showing FTE / Vendor / Tools / AI / Overhead / Rework / Buffer as proportional segments, labeled with $ and %
- A Monte Carlo range — a mini horizontal bar showing P10–Mean–P90 with probability badges:
- P(below target) as a percentage badge
- P(negative) as a red badge if > 5%
- A leakage section — each leakage source as a row with severity badge, monthly $ impact, and fix
- A scenario comparison — 4 cards (Base / Optimistic / Pessimistic / With leakage) each showing margin % and verdict
- An action footer with
sendPrompt()buttons: - "Generate change order to improve margin for [client]" →
change-order-generator - "Resize team to reduce cost for [client]" →
fte-capacity-sizing - "Draft executive memo on commercial risk for [client]" →
executive-growth-memo
Use CSS variables for light/dark mode. Keep it compact — a finance dashboard card.
Markdown Report (Secondary)
After the visual artifact, produce the full simulation as markdown:
## 💰 MARGIN SIMULATION — [Client / Project Name]
### Executive summary
[2-3 sentences: verdict, base margin, Monte Carlo risk, top leakage source, key recommendation]
### Revenue
| Metric | Value |
|--------|-------|
| Monthly revenue | $[X] |
| Contract term | [N] months |
| Total contract value | $[X] |
### Cost structure
| Category | Monthly | % of revenue | Notes |
|----------|---------|-------------|-------|
| FTE / labor | $[X] | [Y]% | [role breakdown] |
| Vendor / third-party | $[X] | [Y]% | |
| Tools / platforms | $[X] | [Y]% | |
| AI infrastructure | $[X] | [Y]% | Claude, MCP, agents |
| Overhead (12%) | $[X] | [Y]% | |
| Rework buffer (10%) | $[X] | [Y]% | |
| Risk buffer (5%) | $[X] | [Y]% | |
| **Total delivery cost** | **$[X]** | **[Y]%** | |
### Margin
| Metric | Value |
|--------|-------|
| Gross margin | $[X]/mo |
| Margin % | [X]% |
| OI / EBITDA impact | $[X] over [N] months |
| Target margin | [X]% |
| Verdict | [verdict] |
### Monte Carlo simulation (N=5,000)
| Metric | Value |
|--------|-------|
| Mean margin | [X]% |
| Median margin | [X]% |
| P10 (pessimistic) | [X]% |
| P90 (optimistic) | [X]% |
| Std deviation | [X]pp |
| P(below target) | [X]% |
| P(negative) | [X]% |
### Leakage analysis
| Source | Severity | Monthly impact | Fix |
|--------|----------|---------------|-----|
[One row per leakage source]
| **Total leakage** | | **$[X]/mo** | |
| **Adjusted margin** | | **[X]%** | |
### Sensitivity analysis
| Cost driver | Base value | +20% shock | Margin impact |
|------------|-----------|------------|---------------|
[Ranked by absolute impact]
### Scenario comparison
| Scenario | Cost | Margin | Verdict |
|----------|------|--------|---------|
[4 scenarios]
### Recommendations
[Numbered list of specific actions based on verdict, Monte Carlo, leakage, and sensitivity]When Information Is Incomplete
If the user provides only partial cost data:
- Use benchmarks from
references/leakage_and_pricing.mdfor missing categories - If FTE costs are unknown, suggest running
fte-capacity-sizingfirst - Flag estimated values with
⚠️ Estimated - Run the simulation with available data and note confidence level
Examples
Example 1 — Viable retainer:
User: "FreshBrew pays $15K/month. Our team costs $4K/month (from capacity model). No vendor costs. Tools are $300/month. Is this viable?"
→ Total cost: ~$5,800/mo. Margin: 61%. Monte Carlo P10: 48%. Verdict: Highly viable. No leakage if revision cap and CO mechanism are in place.
Example 2 — Thin margin with leakage:
User: "Client pays $25K/month. FTE cost is $14K, vendors $3K, tools $1K. No revision caps, no change order process, no client approval SLA."
→ Base margin: 18% (Tight). 3 leakage sources add $4.5K/mo. Adjusted margin: 0.2%. Monte Carlo P(negative): 35%. Verdict: Underfunded. Recommendation: fix leakage sources first — that alone recovers 18pp of margin.
Example 3 — Non-viable project:
User: "We quoted $50K for a 3-month project. Team cost is $22K/month including senior strategist and creative director."
→ Revenue: $16.7K/mo. Cost: $28K/mo. Margin: -68%. Verdict: Non-viable. Fee would need to be $105K+ to hit 30% margin, or scope must be cut by 60%.
Skill Chaining
| Condition | Next skill |
|---|---|
| Need FTE cost inputs | fte-capacity-sizing (upstream) |
| Leakage requires scope fix | scope-audit or change-order-generator |
| Need to reduce headcount to improve margin | fte-capacity-sizing (resize) |
| Leadership needs commercial risk summary | executive-growth-memo |
| Margin approved, campaign ready | campaign-launch-qa |
Margin Leakage & Pricing Reference
Table of Contents
1. Leakage Taxonomy 2. Margin Benchmarks by Engagement Type 3. Monte Carlo Methodology 4. Pricing Strategy Guide 5. The 70/30 Model Impact on Margin
Leakage Taxonomy
Margin leakage is the difference between what you priced and what you actually spend to deliver. These are the eight most common sources in agency operations, ordered by typical impact:
Tier 1 — High impact (15-25% cost increase)
| Source | Impact | How it happens | How to fix |
|---|---|---|---|
| Scope creep (no CO mechanism) | +20% | Client adds work informally; team absorbs without tracking | Change order process with $ threshold and written approval |
| Ad hoc "as needed" support | +20% | SOW has "as needed" clause; client uses it liberally | Cap ad hoc hours per month; track and report |
| Uncapped revisions | +15% | No revision limit; average 2-3 extra rounds per deliverable | "Up to 2 rounds" clause; hourly billing for overages |
| Seniority inflation | +15% | Senior resources pulled into junior tasks | Right-size team; shift junior tasks to AI agents |
Tier 2 — Medium impact (8-12% cost increase)
| Source | Impact | How it happens | How to fix |
|---|---|---|---|
| Unpriced PM overhead | +12% | PM coordination absorbed by delivery team | Add explicit PM allocation (10-15% of delivery hours) |
| Client approval delays | +10% | No feedback SLA; team idles waiting for approvals | 3-business-day SLA with "deemed approved" clause |
| Vendor cost overruns | +8% | Third-party costs exceed estimates | Vendor cost caps with client pass-through |
Tier 3 — Low impact (3-7% cost increase)
| Source | Impact | How it happens | How to fix |
|---|---|---|---|
| Tool costs absorbed | +5% | SaaS licenses not passed to client | Add technology line item or pass-through clause |
| Meeting bloat | +5% | Excessive internal/client meetings not scoped | Define meeting cadence in SOW (e.g., 1 weekly, 1 monthly) |
| Knowledge transfer / ramp-up | +3% | Team turnover creates re-learning overhead | Documentation practices; AI knowledge base |
Cumulative Effect
Leakage compounds. An engagement with 3 high-impact sources can lose 40-50% of margin:
| Scenario | Leakage | Result on 30% margin |
|---|---|---|
| Clean (no leakage) | 0% | 30% margin maintained |
| 1 medium source | ~10% | Margin drops to ~20% |
| 2 medium + 1 high | ~35% | Margin drops to ~5% |
| 3 high sources | ~50% | Margin goes negative |
Margin Benchmarks
Target margins vary by engagement type and market:
| Engagement | Target margin | Acceptable | Red flag |
|---|---|---|---|
| Project-based (fixed fee) | 35-45% | 25% | <15% |
| Monthly retainer | 30-40% | 20% | <10% |
| Annual program | 25-35% | 18% | <10% |
| Performance / media | 15-25% | 12% | <8% |
| Pitch / spec work | 0% (investment) | n/a | >$50K cost |
Cost Structure Benchmarks (% of revenue)
| Cost component | Healthy | Stretched | Bloated |
|---|---|---|---|
| FTE / labor | 40-55% | 55-65% | >65% |
| Vendor / third-party | 5-15% | 15-25% | >25% |
| Tools / platforms | 2-5% | 5-8% | >8% |
| Overhead | 8-12% | 12-18% | >18% |
| Rework / buffer | 5-10% | 10-15% | >15% |
Monte Carlo Methodology
The simulation uses Monte Carlo to model margin uncertainty. This is important because agency P&L is not deterministic — scope creep, client delays, vendor overruns, and rework rates are stochastic events.
Why Monte Carlo (not deterministic scenarios)
A traditional 3-scenario model (base / optimistic / pessimistic) picks arbitrary points. Monte Carlo draws 5,000 samples from probability distributions, giving you:
- Mean and median margin: Where you'll most likely land
- P10 / P90 range: The corridor of plausible outcomes
- Probability of falling below target: Risk quantification
- Probability of negative margin: Worst-case financial exposure
How the Simulation Works
For each of N=5,000 runs:
1. Cost uncertainty: Each cost line is drawn from Normal(base, σ) where σ = uncertainty_pct × base 2. Overhead: Applied as a fixed percentage on drawn FTE + vendor costs 3. Scope creep event: Bernoulli(scope_creep_rate). If triggered, cost increases by Uniform(5%, 25%) 4. Client delay event: Bernoulli(client_delay_rate). If triggered, cost increases by Uniform(3%, 12%) 5. Rework: Drawn from Normal(rework_rate, rework_rate × 0.3) — rework is itself uncertain 6. Margin = (Revenue - Total Cost) / Revenue × 100
Interpreting Results
| Metric | What it tells you |
|---|---|
| Mean margin | Expected average outcome |
| P10 margin | "Bad month" scenario — 90% of outcomes are better than this |
| P90 margin | "Good month" scenario — only 10% of outcomes are better |
| Prob below target | Risk of underperformance — if >50%, the engagement is structurally underfunded |
| Prob negative | Financial risk — if >10%, this engagement could lose money |
| Std deviation | Volatility — high σ means unpredictable margin, needs tighter controls |
When to Trust the Simulation
- High confidence: FTE costs (known rates), tool costs (fixed subscriptions)
- Medium confidence: Vendor costs (quotes exist but may shift), rework rates (historical data)
- Low confidence: Scope creep (depends on client behavior), client delays (unpredictable)
Set uncertainty_pct higher for low-confidence inputs to widen the distribution.
Pricing Strategy Guide
Cost-plus pricing
Price = Total delivery cost × (1 + target margin)Example: $15K cost × (1 + 0.30) = $19,500/month fee
Value-based pricing
Price against the value delivered, not the cost:
- If your media optimization saves the client $200K/year, pricing at $50K/year (25% of value) is defensible even if cost is $15K/year
- Requires proving the value — use
fte-capacity-sizingto show the AI model's efficiency advantage
Retainer pricing guardrails
- Floor: Total cost × 1.20 (minimum 20% margin even in worst case)
- Target: Total cost × 1.45 (30-35% margin with buffer)
- Ceiling: Limited by market rates and client's alternative options
When to walk away
- Margin < 10% with no leakage protections
- Monte Carlo P(negative) > 25%
- Client has documented history of scope creep and no CO mechanism
- FTE cost alone exceeds 70% of fee
70/30 Model Impact
The 70/30 AI-to-human model has a direct structural effect on margin:
Traditional vs. 70/30 comparison
| Metric | Traditional | 70/30 Model | Delta |
|---|---|---|---|
| FTE for 500h/mo scope | 3.0 | 0.9 | -70% |
| Monthly FTE cost (mid) | $7,500 | $2,250 | -$5,250 |
| AI infrastructure cost | $0 | $500 | +$500 |
| Net cost reduction | — | — | -$4,750/mo |
| Margin on $15K fee | 28% | 72% | +44pp |
Where the savings come from
1. Fewer human hours → lower FTE cost (the primary driver) 2. Higher seniority concentration → fewer people but more experienced (quality up) 3. AI handles volume → humans handle exceptions (utilization optimized) 4. Fixed AI cost → marginal cost per additional deliverable approaches zero
What to reinvest
The margin surplus from the 70/30 model can be deployed:
- Quality uplift: Staff senior resources instead of juniors
- Price competitiveness: Win pitches with lower fees
- Scope generosity: Include more deliverables at the same fee
- Profit retention: Improve agency EBITDA directly
#!/usr/bin/env python3
"""
Margin Simulation Engine
========================
Models agency profitability at the client/scope/campaign level. Combines a
deterministic P&L calculator with Monte Carlo simulation for uncertainty
quantification, leakage detection, and sensitivity analysis.
Architecture:
- Deterministic layer: Standard P&L (revenue - costs = margin)
- Stochastic layer: Monte Carlo (N=5000 draws) models uncertainty in
scope creep, rework, client delays, and vendor overruns
- Leakage detector: Flags margin erosion sources with estimated impact
- Sensitivity analyzer: Tests which cost driver has the largest marginal
impact on margin — answers "where should I negotiate?"
The 70/30 AI model is embedded: FTE costs reflect human hours only (after AI
automation), and AI infrastructure cost is modeled as a fixed overhead.
Usage:
from scripts.margin_engine import MarginSimulator
sim = MarginSimulator(config)
result = sim.run()
# CLI:
python margin_engine.py --input config.json --output margin.json
"""
import json
import sys
import argparse
import math
from dataclasses import dataclass, field, asdict
from typing import Optional
# Try numpy for Monte Carlo; fall back to pure Python if unavailable
try:
import numpy as np
HAS_NUMPY = True
except ImportError:
HAS_NUMPY = False
import random
# ── Constants ───────────────────────────────────────────────────────────────
DEFAULT_MONTE_CARLO_RUNS = 5000
AGENCY_OVERHEAD_PCT = 0.12 # 12% agency overhead on delivery cost
AI_INFRASTRUCTURE_MONTHLY = 500 # Base AI tooling cost (Claude, MCP, infra)
DEFAULT_MARGIN_TARGET = 0.30 # 30% target gross margin
DEFAULT_REWORK_RATE = 0.10 # 10% rework buffer
DEFAULT_SCOPE_CREEP_RATE = 0.15 # 15% scope creep probability per month
DEFAULT_CLIENT_DELAY_RATE = 0.20 # 20% probability of client-caused delays
# ── Leakage Patterns ────────────────────────────────────────────────────────
LEAKAGE_PATTERNS = {
"no_revision_cap": {
"name": "Uncapped revisions",
"impact_pct": 0.15, # 15% cost increase
"description": "No revision cap → average 2-3 extra rounds per deliverable",
"fix": "Add 'up to 2 rounds' clause; bill overages at hourly rate",
},
"no_pm_hours": {
"name": "Unpriced PM overhead",
"impact_pct": 0.12,
"description": "Project management not explicitly costed → absorbed by team",
"fix": "Add explicit PM allocation at 10-15% of delivery hours",
},
"scope_creep": {
"name": "Scope creep (no CO mechanism)",
"impact_pct": 0.20,
"description": "No change order process → additional work absorbed at no charge",
"fix": "Implement change order threshold and approval workflow",
},
"client_delays": {
"name": "Client approval delays",
"impact_pct": 0.10,
"description": "No client SLA → team idles waiting for feedback, hours still burn",
"fix": "Add 3-business-day approval SLA with deemed-approved clause",
},
"vendor_overruns": {
"name": "Vendor cost overruns",
"impact_pct": 0.08,
"description": "Third-party costs exceed estimates (media, production, tech)",
"fix": "Add vendor cost caps with client pass-through for overages",
},
"seniority_mismatch": {
"name": "Seniority inflation",
"impact_pct": 0.15,
"description": "Senior resources pulled into junior tasks due to capacity gaps",
"fix": "Right-size team; use AI agents for junior-level execution",
},
"as_needed_support": {
"name": "Ad hoc 'as needed' work",
"impact_pct": 0.20,
"description": "'As needed' clause consumed without tracking → invisible hours",
"fix": "Cap ad hoc hours at N/month; track and report monthly",
},
"tool_costs_unpriced": {
"name": "Tool/platform costs absorbed",
"impact_pct": 0.05,
"description": "SaaS licenses, API costs, platform fees not passed to client",
"fix": "Add technology line item or pass-through clause",
},
}
# ── Data Classes ────────────────────────────────────────────────────────────
@dataclass
class CostLine:
"""A single cost component."""
category: str # fte, vendor, overhead, tools, rework, buffer
name: str
monthly_amount: float
notes: str = ""
is_variable: bool = False # Variable costs scale with scope changes
uncertainty_pct: float = 0.0 # ±% for Monte Carlo draws
@dataclass
class LeakageItem:
"""A detected margin leakage source."""
pattern: str
name: str
monthly_impact: float
impact_pct: float
description: str
fix: str
severity: str = "Medium" # Low, Medium, High
@dataclass
class ScenarioResult:
"""Result of a single P&L scenario."""
name: str
monthly_revenue: float
total_cost: float
gross_margin: float
margin_pct: float
oi_ebitda: float
verdict: str
@dataclass
class MonteCarloResult:
"""Summary of Monte Carlo simulation."""
runs: int
mean_margin_pct: float
median_margin_pct: float
p10_margin_pct: float # 10th percentile (pessimistic)
p90_margin_pct: float # 90th percentile (optimistic)
std_margin_pct: float
prob_below_target: float # P(margin < target)
prob_negative: float # P(margin < 0)
@dataclass
class SensitivityItem:
"""Sensitivity of margin to a cost driver."""
driver: str
base_value: float
delta_pct: float # % change applied
margin_impact_pct: float # Resulting margin change in percentage points
direction: str # "up" or "down"
@dataclass
class MarginResult:
"""Full margin simulation output."""
client_name: str = ""
engagement_type: str = "" # retainer, project, campaign
# Revenue
monthly_revenue: float = 0
contract_months: int = 12
total_contract_value: float = 0
# Deterministic P&L
fte_cost: float = 0
vendor_cost: float = 0
tool_cost: float = 0
overhead_cost: float = 0
ai_infrastructure_cost: float = 0
rework_buffer: float = 0
risk_buffer: float = 0
total_delivery_cost: float = 0
gross_margin: float = 0
margin_pct: float = 0
oi_ebitda: float = 0
margin_target: float = 0.30
# Cost breakdown
cost_lines: list = field(default_factory=list)
# Leakage
leakage_items: list = field(default_factory=list)
total_leakage: float = 0
adjusted_margin_pct: float = 0
# Monte Carlo
monte_carlo: dict = field(default_factory=dict)
# Sensitivity
sensitivity: list = field(default_factory=list)
# Scenarios
scenarios: list = field(default_factory=list)
# Verdict
verdict: str = ""
verdict_rationale: str = ""
recommendations: list = field(default_factory=list)
def to_dict(self):
d = asdict(self)
# Round all floats
def round_floats(obj, decimals=2):
if isinstance(obj, float):
return round(obj, decimals)
elif isinstance(obj, dict):
return {k: round_floats(v, decimals) for k, v in obj.items()}
elif isinstance(obj, list):
return [round_floats(i, decimals) for i in obj]
return obj
return round_floats(d)
def to_json(self, indent=2):
return json.dumps(self.to_dict(), indent=indent, ensure_ascii=False)
# ── Simulator ───────────────────────────────────────────────────────────────
class MarginSimulator:
"""
Main margin simulation engine.
Config dict expected keys:
client_name: str
engagement_type: str (retainer|project|campaign)
monthly_revenue: float
contract_months: int (default 12)
margin_target: float (default 0.30)
# Cost inputs (all monthly)
fte_costs: list[dict] — [{name, monthly_amount, seniority, fte, uncertainty_pct}]
vendor_costs: list[dict] — [{name, monthly_amount, uncertainty_pct}]
tool_costs: list[dict] — [{name, monthly_amount}]
overhead_pct: float (default 0.12)
ai_infrastructure: float (default 500)
rework_rate: float (default 0.10)
risk_buffer_pct: float (default 0.05)
# Leakage flags (optional booleans)
has_revision_cap: bool
has_pm_hours: bool
has_change_order: bool
has_client_sla: bool
has_vendor_caps: bool
has_seniority_match: bool
has_adhoc_cap: bool
has_tool_passthrough: bool
# Monte Carlo settings
monte_carlo_runs: int (default 5000)
scope_creep_rate: float (default 0.15)
client_delay_rate: float (default 0.20)
"""
def __init__(self, config: dict):
self.config = config
self.result = MarginResult(
client_name=config.get("client_name", "Unknown"),
engagement_type=config.get("engagement_type", "retainer"),
monthly_revenue=config.get("monthly_revenue", 0),
contract_months=config.get("contract_months", 12),
margin_target=config.get("margin_target", DEFAULT_MARGIN_TARGET),
)
def run(self) -> MarginResult:
"""Execute full margin simulation pipeline."""
self._build_cost_structure()
self._calculate_deterministic_pnl()
self._detect_leakage()
self._run_monte_carlo()
self._run_sensitivity()
self._build_scenarios()
self._determine_verdict()
return self.result
def _build_cost_structure(self):
"""Assemble all cost lines from config."""
lines = []
# FTE costs
for fte in self.config.get("fte_costs", []):
lines.append(CostLine(
category="fte",
name=fte.get("name", "FTE"),
monthly_amount=fte.get("monthly_amount", 0),
notes=f"{fte.get('fte', '?')} FTE @ {fte.get('seniority', 'mid')}",
is_variable=True,
uncertainty_pct=fte.get("uncertainty_pct", 10),
))
# Vendor costs
for v in self.config.get("vendor_costs", []):
lines.append(CostLine(
category="vendor",
name=v.get("name", "Vendor"),
monthly_amount=v.get("monthly_amount", 0),
is_variable=True,
uncertainty_pct=v.get("uncertainty_pct", 15),
))
# Tool costs
for t in self.config.get("tool_costs", []):
lines.append(CostLine(
category="tools",
name=t.get("name", "Tool"),
monthly_amount=t.get("monthly_amount", 0),
is_variable=False,
uncertainty_pct=5,
))
# AI infrastructure
ai_cost = self.config.get("ai_infrastructure", AI_INFRASTRUCTURE_MONTHLY)
lines.append(CostLine(
category="ai",
name="AI infrastructure (Claude, MCP, agents)",
monthly_amount=ai_cost,
is_variable=False,
uncertainty_pct=10,
))
self.result.cost_lines = lines
def _calculate_deterministic_pnl(self):
"""Calculate the base-case P&L."""
rev = self.result.monthly_revenue
fte_cost = sum(c.monthly_amount for c in self.result.cost_lines if c.category == "fte")
vendor_cost = sum(c.monthly_amount for c in self.result.cost_lines if c.category == "vendor")
tool_cost = sum(c.monthly_amount for c in self.result.cost_lines if c.category == "tools")
ai_cost = sum(c.monthly_amount for c in self.result.cost_lines if c.category == "ai")
# Overhead
delivery_base = fte_cost + vendor_cost
overhead_pct = self.config.get("overhead_pct", AGENCY_OVERHEAD_PCT)
overhead = delivery_base * overhead_pct
# Rework buffer
rework_rate = self.config.get("rework_rate", DEFAULT_REWORK_RATE)
rework = fte_cost * rework_rate
# Risk buffer
risk_pct = self.config.get("risk_buffer_pct", 0.05)
risk_buffer = delivery_base * risk_pct
total_cost = fte_cost + vendor_cost + tool_cost + ai_cost + overhead + rework + risk_buffer
self.result.fte_cost = fte_cost
self.result.vendor_cost = vendor_cost
self.result.tool_cost = tool_cost
self.result.ai_infrastructure_cost = ai_cost
self.result.overhead_cost = overhead
self.result.rework_buffer = rework
self.result.risk_buffer = risk_buffer
self.result.total_delivery_cost = total_cost
self.result.gross_margin = rev - total_cost
self.result.margin_pct = ((rev - total_cost) / rev * 100) if rev > 0 else 0
self.result.oi_ebitda = (rev - total_cost) * self.result.contract_months
self.result.total_contract_value = rev * self.result.contract_months
def _detect_leakage(self):
"""Identify margin leakage sources from config flags."""
flag_map = {
"has_revision_cap": ("no_revision_cap", False),
"has_pm_hours": ("no_pm_hours", False),
"has_change_order": ("scope_creep", False),
"has_client_sla": ("client_delays", False),
"has_vendor_caps": ("vendor_overruns", False),
"has_seniority_match": ("seniority_mismatch", False),
"has_adhoc_cap": ("as_needed_support", False),
"has_tool_passthrough": ("tool_costs_unpriced", False),
}
delivery_cost = self.result.total_delivery_cost
total_leakage = 0
for config_key, (pattern_key, safe_default) in flag_map.items():
has_protection = self.config.get(config_key, safe_default)
if not has_protection:
pattern = LEAKAGE_PATTERNS[pattern_key]
impact = delivery_cost * pattern["impact_pct"]
severity = "High" if pattern["impact_pct"] >= 0.15 else "Medium" if pattern["impact_pct"] >= 0.08 else "Low"
self.result.leakage_items.append(LeakageItem(
pattern=pattern_key,
name=pattern["name"],
monthly_impact=impact,
impact_pct=pattern["impact_pct"] * 100,
description=pattern["description"],
fix=pattern["fix"],
severity=severity,
))
total_leakage += impact
self.result.total_leakage = total_leakage
adjusted_cost = self.result.total_delivery_cost + total_leakage
rev = self.result.monthly_revenue
self.result.adjusted_margin_pct = ((rev - adjusted_cost) / rev * 100) if rev > 0 else 0
def _run_monte_carlo(self):
"""Run Monte Carlo simulation to quantify margin uncertainty."""
n = self.config.get("monte_carlo_runs", DEFAULT_MONTE_CARLO_RUNS)
rev = self.result.monthly_revenue
if rev <= 0:
self.result.monte_carlo = {}
return
scope_creep_rate = self.config.get("scope_creep_rate", DEFAULT_SCOPE_CREEP_RATE)
client_delay_rate = self.config.get("client_delay_rate", DEFAULT_CLIENT_DELAY_RATE)
margins = []
if HAS_NUMPY:
rng = np.random.default_rng(42)
for _ in range(n):
total = 0
for c in self.result.cost_lines:
unc = c.uncertainty_pct / 100
drawn = c.monthly_amount * (1 + rng.normal(0, unc))
total += max(drawn, 0)
# Overhead on drawn costs
overhead_pct = self.config.get("overhead_pct", AGENCY_OVERHEAD_PCT)
total *= (1 + overhead_pct)
# Scope creep event
if rng.random() < scope_creep_rate:
creep_factor = rng.uniform(0.05, 0.25)
total *= (1 + creep_factor)
# Client delay event
if rng.random() < client_delay_rate:
delay_cost = total * rng.uniform(0.03, 0.12)
total += delay_cost
# Rework
rework_rate = self.config.get("rework_rate", DEFAULT_REWORK_RATE)
total *= (1 + rng.normal(rework_rate, rework_rate * 0.3))
margin = (rev - total) / rev * 100
margins.append(margin)
margins = np.array(margins)
self.result.monte_carlo = asdict(MonteCarloResult(
runs=n,
mean_margin_pct=float(np.mean(margins)),
median_margin_pct=float(np.median(margins)),
p10_margin_pct=float(np.percentile(margins, 10)),
p90_margin_pct=float(np.percentile(margins, 90)),
std_margin_pct=float(np.std(margins)),
prob_below_target=float(np.mean(margins < self.result.margin_target * 100)),
prob_negative=float(np.mean(margins < 0)),
))
else:
# Pure Python fallback
random.seed(42)
for _ in range(n):
total = 0
for c in self.result.cost_lines:
unc = c.uncertainty_pct / 100
drawn = c.monthly_amount * (1 + random.gauss(0, unc))
total += max(drawn, 0)
overhead_pct = self.config.get("overhead_pct", AGENCY_OVERHEAD_PCT)
total *= (1 + overhead_pct)
if random.random() < scope_creep_rate:
total *= (1 + random.uniform(0.05, 0.25))
if random.random() < client_delay_rate:
total += total * random.uniform(0.03, 0.12)
rework_rate = self.config.get("rework_rate", DEFAULT_REWORK_RATE)
total *= (1 + random.gauss(rework_rate, rework_rate * 0.3))
margins.append((rev - total) / rev * 100)
margins.sort()
mean_m = sum(margins) / n
median_m = margins[n // 2]
p10 = margins[int(n * 0.10)]
p90 = margins[int(n * 0.90)]
variance = sum((m - mean_m) ** 2 for m in margins) / n
std_m = math.sqrt(variance)
prob_below = sum(1 for m in margins if m < self.result.margin_target * 100) / n
prob_neg = sum(1 for m in margins if m < 0) / n
self.result.monte_carlo = asdict(MonteCarloResult(
runs=n, mean_margin_pct=mean_m, median_margin_pct=median_m,
p10_margin_pct=p10, p90_margin_pct=p90, std_margin_pct=std_m,
prob_below_target=prob_below, prob_negative=prob_neg,
))
def _run_sensitivity(self):
"""Test which cost driver moves margin the most (±20% shock)."""
rev = self.result.monthly_revenue
base_cost = self.result.total_delivery_cost
base_margin = self.result.margin_pct
delta = 0.20 # 20% shock
if rev <= 0:
return
drivers = {}
for c in self.result.cost_lines:
cat = c.category
if cat not in drivers:
drivers[cat] = 0
drivers[cat] += c.monthly_amount
for driver, amount in drivers.items():
if amount <= 0:
continue
shocked_cost = base_cost + (amount * delta)
shocked_margin = (rev - shocked_cost) / rev * 100
impact = shocked_margin - base_margin
self.result.sensitivity.append(asdict(SensitivityItem(
driver=driver,
base_value=amount,
delta_pct=delta * 100,
margin_impact_pct=impact,
direction="up",
)))
# Sort by absolute impact (most sensitive first)
self.result.sensitivity.sort(key=lambda x: abs(x["margin_impact_pct"]), reverse=True)
def _build_scenarios(self):
"""Generate base, optimistic, and pessimistic scenarios."""
rev = self.result.monthly_revenue
base = self.result.total_delivery_cost
scenarios = [
("Base case", base, 0),
("Optimistic (AI uplift, no creep)", base * 0.85, -15),
("Pessimistic (scope creep + rework)", base * 1.30, 30),
("With leakage (no protections)", base + self.result.total_leakage, 0),
]
for name, cost, note in scenarios:
margin = rev - cost
margin_pct = (margin / rev * 100) if rev > 0 else 0
verdict = (
"Viable" if margin_pct >= 30 else
"Tight" if margin_pct >= 15 else
"Underfunded" if margin_pct >= 0 else
"Non-viable"
)
self.result.scenarios.append(asdict(ScenarioResult(
name=name,
monthly_revenue=rev,
total_cost=cost,
gross_margin=margin,
margin_pct=margin_pct,
oi_ebitda=margin * self.result.contract_months,
verdict=verdict,
)))
def _determine_verdict(self):
"""Issue overall commercial verdict."""
margin = self.result.margin_pct
mc = self.result.monte_carlo
leakage_count = len(self.result.leakage_items)
high_leakage = sum(1 for l in self.result.leakage_items if l.severity == "High")
recs = []
# Margin-based verdict
if margin >= 40:
self.result.verdict = "Highly viable"
self.result.verdict_rationale = (
f"Base margin at {margin:.1f}% — well above {self.result.margin_target*100:.0f}% target. "
f"Strong commercial position."
)
elif margin >= self.result.margin_target * 100:
self.result.verdict = "Viable"
self.result.verdict_rationale = (
f"Base margin at {margin:.1f}% — meets {self.result.margin_target*100:.0f}% target."
)
elif margin >= 15:
self.result.verdict = "Tight"
self.result.verdict_rationale = (
f"Base margin at {margin:.1f}% — below target. "
f"Vulnerable to scope creep and rework."
)
recs.append("Increase fee by 10-20% or reduce deliverable count.")
elif margin >= 0:
self.result.verdict = "Underfunded"
self.result.verdict_rationale = (
f"Base margin at {margin:.1f}% — near breakeven. "
f"Any scope creep or rework makes this unprofitable."
)
recs.append("Renegotiate fee or significantly reduce scope.")
recs.append("Shift more tasks to AI-first execution to cut FTE cost.")
else:
self.result.verdict = "Non-viable"
self.result.verdict_rationale = (
f"Negative margin at {margin:.1f}%. "
f"Delivery cost (${self.result.total_delivery_cost:,.0f}/mo) exceeds "
f"revenue (${self.result.monthly_revenue:,.0f}/mo)."
)
recs.append("Reject unless fee is increased or scope fundamentally reduced.")
# Monte Carlo enrichment
if mc:
prob_below = mc.get("prob_below_target", 0) * 100
prob_neg = mc.get("prob_negative", 0) * 100
if prob_below > 50:
recs.append(
f"Monte Carlo: {prob_below:.0f}% probability of falling below target margin. "
f"P10 margin: {mc.get('p10_margin_pct', 0):.1f}%."
)
if prob_neg > 10:
recs.append(
f"Monte Carlo: {prob_neg:.0f}% probability of negative margin — significant financial risk."
)
# Leakage warnings
if high_leakage > 0:
recs.append(
f"{high_leakage} high-severity leakage source(s) detected. "
f"Total monthly leakage: ${self.result.total_leakage:,.0f}. "
f"Adjusted margin would be {self.result.adjusted_margin_pct:.1f}%."
)
# Sensitivity insight
if self.result.sensitivity:
top = self.result.sensitivity[0]
recs.append(
f"Most sensitive cost driver: '{top['driver']}' — "
f"a 20% increase would reduce margin by {abs(top['margin_impact_pct']):.1f}pp."
)
self.result.recommendations = recs
# ── CLI Entry Point ─────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(description="Margin Simulation Engine")
parser.add_argument("--input", "-i", required=True, help="JSON config file")
parser.add_argument("--output", "-o", default=None, help="Output JSON path")
args = parser.parse_args()
with open(args.input, "r", encoding="utf-8") as f:
config = json.load(f)
sim = MarginSimulator(config)
result = sim.run()
output = result.to_json()
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output)
print(f"✅ Margin simulation complete → {args.output}")
print(f" Verdict: {result.verdict}")
print(f" Revenue: ${result.monthly_revenue:,.0f}/mo | Cost: ${result.total_delivery_cost:,.0f}/mo")
print(f" Margin: {result.margin_pct:.1f}% | OI/EBITDA: ${result.oi_ebitda:,.0f}")
if result.monte_carlo:
mc = result.monte_carlo
print(f" Monte Carlo: P10={mc['p10_margin_pct']:.1f}% | Mean={mc['mean_margin_pct']:.1f}% | P90={mc['p90_margin_pct']:.1f}%")
print(f" Leakage: ${result.total_leakage:,.0f}/mo ({len(result.leakage_items)} sources)")
else:
print(output)
if __name__ == "__main__":
main()
Related skills
FAQ
How does margin-simulation handle uncertainty?
It runs a Monte Carlo simulation with 5,000 draws by default to produce stable margin percentiles.
What is leakage detection?
It flags margin-erosion sources across a taxonomy of 8 patterns and 3 severity tiers.