Waterfall Chart
Revenue Recognition Timing Analysis
ASC 606 revenue recognition waterfall showing timing of revenue recognition.
Output
Python
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Patch
categories = ['Contract\nValue', 'Upfront\nRecognition', 'Q1\nRecognition', 'Q2\nRecognition',
'Q3\nRecognition', 'Q4\nRecognition', 'Deferred\nRevenue']
values = [0, -180, -95, -88, -75, -62, 0]
initial = 500
running_total = initial
bottoms, heights, colors = [], [], []
palette = ['#3b82f6', '#8b5cf6', '#06b6d4', '#22c55e', '#f59e0b']
for i, (cat, val) in enumerate(zip(categories, values)):
if 'Contract' in cat:
bottoms.append(0)
heights.append(initial)
colors.append('#3b82f6')
elif 'Deferred' in cat:
bottoms.append(0)
heights.append(running_total)
colors.append('#f59e0b')
else:
bottoms.append(running_total + val)
heights.append(abs(val))
colors.append(palette[(i) % len(palette)])
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 'Contract' in categories[i] or 'Deferred' in categories[i]:
label = str(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 = str(abs(val)) + "M"
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]
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=10, color='#374151')
ax.set_ylabel('Revenue (Millions)', fontsize=12, color='#374151', fontweight='500')
ax.set_title('ASC 606 Revenue Recognition Timing', 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')
recognized = initial - running_total
pct = (recognized / initial) * 100
label_text = 'Recognized: ' + str(recognized) + 'M (' + str(int(pct)) + ' pct)'
ax.annotate(label_text, xy=(0.5, 0.95), xycoords='axes fraction',
fontsize=10, color='#374151', ha='center', fontweight='bold',
bbox=dict(boxstyle='round,pad=0.4', facecolor='#f3f4f6', edgecolor='#d1d5db'))
legend_elements = [Patch(facecolor='#3b82f6', label='Contract Value'), Patch(facecolor='#8b5cf6', label='Revenue Recognized'),
Patch(facecolor='#f59e0b', label='Deferred Revenue')]
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
More Waterfall Chart examples
☕