Waterfall Chart

Working Capital Changes Analysis

Light-themed waterfall chart showing working capital changes from operating activities.

Output
Working Capital Changes Analysis
Python
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Patch

# Working capital changes (in millions)
categories = ['Beginning\nWC', 'Receivables', 'Inventory', 'Prepaid\nExpenses', 
              'Payables', 'Accrued\nLiabilities', 'Deferred\nRevenue', 'Ending\nWC']
values = [0, -25, -18, -5, 32, 15, 8, 0]

# Calculate running total
initial = 180
running_total = initial
bottoms = []
heights = []
colors = []

for i, (cat, val) in enumerate(zip(categories, values)):
    if 'Beginning' in cat:
        bottoms.append(0)
        heights.append(initial)
        colors.append('#3b82f6')
    elif 'Ending' in cat:
        bottoms.append(0)
        heights.append(running_total)
        colors.append('#8b5cf6')
    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

# Create figure
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)

# Add value labels
for i, (bar, val, bot, height) in enumerate(zip(bars, values, bottoms, heights)):
    y_pos = bot + height / 2
    if 'Beginning' in categories[i] or 'Ending' in categories[i]:
        label = f"${height}M"
        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}M" if val > 0 else f"-${abs(val)}M"
        ax.text(bar.get_x() + bar.get_width()/2, y_pos, label, 
                ha='center', va='center', fontsize=9, fontweight='bold', color='white')

# Connect bars
for i in range(len(x) - 1):
    if i == 0:
        y = initial
    else:
        y = 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)

# Styling
ax.set_xlim(-0.6, len(categories) - 0.4)
ax.set_ylim(0, max(bottoms[i] + heights[i] for i in range(len(heights))) * 1.1)
ax.set_xticks(x)
ax.set_xticklabels(categories, fontsize=9, color='#374151')
ax.set_ylabel('Working Capital ($ Millions)', fontsize=12, color='#374151', fontweight='500')
ax.set_title('Working Capital Movement 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')

# Cash impact annotation
cash_impact = running_total - initial
direction = "Source" if cash_impact > 0 else "Use"
ax.annotate(f'Net Cash {direction}: ${abs(cash_impact)}M', xy=(0.98, 0.95), xycoords='axes fraction',
            fontsize=11, color='#16a34a' if cash_impact > 0 else '#dc2626', ha='right', fontweight='bold',
            bbox=dict(boxstyle='round,pad=0.4', facecolor='#f0fdf4' if cash_impact > 0 else '#fef2f2', 
                     edgecolor='#22c55e' if cash_impact > 0 else '#ef4444'))

# Legend outside plot
legend_elements = [
    Patch(facecolor='#3b82f6', label='Beginning WC'),
    Patch(facecolor='#22c55e', label='Cash Source'),
    Patch(facecolor='#ef4444', label='Cash Use'),
    Patch(facecolor='#8b5cf6', label='Ending WC')
]
ax.legend(handles=legend_elements, loc='upper left', bbox_to_anchor=(0, -0.1), 
          ncol=4, 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