Waterfall Chart

Startup Funding Runway Analysis

Cash runway waterfall showing burn rate components and remaining runway.

Output
Startup Funding Runway Analysis
Python
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Patch

categories = ['Series A\nFunding', 'Engineering\nTeam', 'Sales &\nMarketing', 'Operations', 
              'Infrastructure', 'Legal &\nAdmin', 'Revenue\nOffset', 'Remaining\nRunway']
values = [0, -180, -120, -45, -35, -25, 85, 0]

initial = 500
running_total = initial
bottoms, heights, colors = [], [], []

for i, (cat, val) in enumerate(zip(categories, values)):
    if 'Series' in cat:
        bottoms.append(0)
        heights.append(initial)
        colors.append('#22c55e')
    elif 'Remaining' in cat:
        bottoms.append(0)
        heights.append(running_total)
        colors.append('#3b82f6' if running_total > 100 else '#f59e0b')
    elif val > 0:
        bottoms.append(running_total)
        heights.append(val)
        colors.append('#22c55e')
        running_total += val
    else:
        bottoms.append(running_total + val)
        heights.append(abs(val))
        colors.append('#ef4444')
        running_total += val

fig, ax = plt.subplots(figsize=(14, 8), facecolor='#ffffff')
ax.set_facecolor('#ffffff')

x = np.arange(len(categories))
bars = ax.bar(x, heights, bottom=bottoms, color=colors, width=0.65, edgecolor='#e5e7eb', linewidth=1)

for i, (bar, val, bot, height) in enumerate(zip(bars, values, bottoms, heights)):
    y_pos = bot + height / 2
    if 'Series' in categories[i] or 'Remaining' in categories[i]:
        label = f"${height}K"
        ax.text(bar.get_x() + bar.get_width()/2, y_pos, label, ha='center', va='center', 
                fontsize=11, fontweight='bold', color='white')
    else:
        label = f"+${val}K" if val > 0 else f"-${abs(val)}K"
        ax.text(bar.get_x() + bar.get_width()/2, y_pos, label, ha='center', va='center', 
                fontsize=10, fontweight='bold', color='white')

for i in range(len(x) - 1):
    y = initial if i == 0 else bottoms[i] + heights[i]
    ax.plot([x[i] + 0.35, x[i+1] - 0.35], [y, y], color='#9ca3af', linestyle='--', linewidth=1.5, alpha=0.7)

ax.set_xlim(-0.6, len(categories) - 0.4)
ax.set_ylim(0, initial * 1.1)
ax.set_xticks(x)
ax.set_xticklabels(categories, fontsize=9, color='#374151')
ax.set_ylabel('Cash ($ Thousands)', fontsize=12, color='#374151', fontweight='500')
ax.set_title('12-Month Cash Runway Analysis', fontsize=16, color='#111827', fontweight='bold', pad=20)
ax.tick_params(axis='y', colors='#374151', labelsize=10)
ax.yaxis.grid(True, linestyle='--', alpha=0.4, color='#e5e7eb')
ax.set_axisbelow(True)
for spine in ax.spines.values():
    spine.set_color('#d1d5db')

# Calculate runway in months
monthly_burn = (initial - running_total - 85) / 12  # Net of revenue
runway_months = running_total / monthly_burn if monthly_burn > 0 else 0
ax.annotate(f'Monthly Burn: ${monthly_burn:.0f}K | Runway: {runway_months:.0f} months', xy=(0.98, 0.95), xycoords='axes fraction',
            fontsize=10, color='#2563eb', ha='right', fontweight='bold',
            bbox=dict(boxstyle='round,pad=0.4', facecolor='#eff6ff', edgecolor='#3b82f6'))

legend_elements = [Patch(facecolor='#22c55e', label='Funding/Revenue'), Patch(facecolor='#ef4444', label='Burn Rate'),
                   Patch(facecolor='#3b82f6', label='Remaining Cash')]
ax.legend(handles=legend_elements, loc='upper left', bbox_to_anchor=(0, -0.1), ncol=3, fontsize=9,
          facecolor='white', edgecolor='#d1d5db', labelcolor='#374151')

plt.tight_layout()
plt.subplots_adjust(bottom=0.15)
plt.show()
Library

Matplotlib

Category

Financial

Did this help you?

Support PyLucid to keep it free & growing

Support