This document provides step-by-step instructions for executing Task 4 (Chart Generation) of the initiating-coverage skill.
Purpose: Generate 25-35 professional financial charts for the report.
Prerequisites: ⚠️ Verify before starting
⚠️ CRITICAL: DO NOT START THIS TASK UNLESS TASKS 1, 2, AND 3 ARE COMPLETE
This task requires outputs from all three previous tasks. Starting without them will result in incomplete charts.
IF ANY OF TASKS 1, 2, OR 3 ARE NOT COMPLETE: Stop immediately and inform the user which tasks need to be completed first. The specific requirements are:
Do not attempt to create placeholder charts or skip charts due to missing data.
Output: 25-35 Professional Chart Files (PNG/JPG, 300 DPI)
BEFORE STARTING - CHECK ALL PREREQUISITES:
IF ANY VERIFICATION FAILS:
IMPORTANT: Task 5 (Report Assembly) will embed ALL charts created throughout the report. The report requires dense visual content (1 chart every 200-300 words), so create comprehensive chart coverage.
These 4 charts are critical visualizations that MUST be present:
Create all 25 of these charts. Each has a specific purpose in Task 5:
Investment Summary Section (1 chart):
Financial Performance Section (6 charts):
Company 101 Section (7 charts):
Competitive & Market Section (2 charts):
Scenario Analysis Section (2 charts):
Valuation Section (7 charts):
Total: 25 Required Charts
Add these for greater visual density and storytelling (reach 26-35 total):
Total Range: 25-35 Charts (25 required + 0-10 optional)
Understanding where each chart's data comes from:
IMPORTANT: Require ALL three tasks (1, 2, 3) complete PLUS external data access to create all 25 required charts.
Install required libraries:
pip install matplotlib seaborn pandas numpy plotlyCreate Python script header:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
from matplotlib.patches import Rectangle
import warnings
warnings.filterwarnings('ignore')
# Set global style
plt.style.use('seaborn-v0_8-darkgrid')
sns.set_palette("husl")
# Global settings
DPI = 300
FIGURE_WIDTH = 10
FIGURE_HEIGHT = 6
TITLE_FONT_SIZE = 14
AXIS_FONT_SIZE = 12
LABEL_FONT_SIZE = 10# Revenue by Product (from Task 2 model)
years = [2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029]
# Extract from Excel or define manually from model
product_a = [100, 120, 145, 175, 210, 252, 302, 363, 435, 522]
product_b = [80, 95, 115, 138, 165, 198, 238, 285, 342, 411]
product_c = [50, 62, 78, 98, 122, 153, 191, 239, 299, 374]
product_d = [30, 38, 48, 61, 77, 97, 122, 153, 191, 239]
# Revenue by Geography
north_america = [150, 180, 220, 265, 320, 384, 461, 553, 664, 797]
europe = [80, 95, 115, 140, 170, 204, 245, 294, 353, 423]
asia_pacific = [40, 50, 63, 80, 101, 127, 159, 199, 249, 311]
rest_of_world = [20, 25, 32, 40, 51, 64, 80, 100, 125, 156]# Margin evolution
gross_margin = [58.0, 59.2, 60.5, 61.8, 63.0, 64.5, 66.0, 67.0, 67.5, 68.0]
ebitda_margin = [12.0, 15.5, 18.8, 22.0, 25.0, 28.0, 30.5, 32.0, 33.0, 34.0]
fcf_margin = [8.0, 11.0, 14.5, 18.0, 21.0, 24.0, 26.5, 28.0, 29.0, 30.0]# DCF Sensitivity (from Task 3 valuation)
wacc_values = [7.0, 8.0, 9.0, 10.0, 11.0, 12.0]
terminal_growth = [1.5, 2.0, 2.5, 3.0, 3.5]
# Price per share matrix (rows = WACC, columns = terminal growth)
dcf_sensitivity = np.array([
[66, 71, 76, 82, 89],
[58, 62, 67, 72, 78],
[52, 55, 59, 63, 68],
[47, 50, 53, 56, 60],
[42, 45, 48, 51, 54],
[39, 41, 44, 46, 49]
])# Valuation Football Field (from Task 3)
valuation_methods = ['DCF Analysis', 'Trading Comps\n(NTM)', 'Precedent\nTransactions']
valuation_low = [48, 45, 52]
valuation_high = [62, 57, 66]
current_price = 50
target_price = 55def create_revenue_by_product_chart():
"""Create revenue by product stacked area chart"""
fig, ax = plt.subplots(figsize=(10, 6))
# Create stacked area chart
ax.stackplot(years, product_a, product_b, product_c, product_d,
labels=['Product A', 'Product B', 'Product C', 'Product D'],
colors=['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728'],
alpha=0.8)
# Formatting
ax.set_xlabel('Year', fontsize=12, fontweight='bold')
ax.set_ylabel('Revenue ($M)', fontsize=12, fontweight='bold')
ax.set_title('Figure 3 - Revenue by Product/Segment (2020-2029E)',
fontsize=14, fontweight='bold', pad=20)
# Legend
ax.legend(loc='upper left', frameon=False, fontsize=10)
# Grid
ax.grid(axis='y', alpha=0.3, linestyle='--')
ax.set_axisbelow(True)
# Remove top and right spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Add vertical line to separate historical from projected
ax.axvline(x=2024, color='gray', linestyle='--', linewidth=1, alpha=0.5)
ax.text(2024.2, ax.get_ylim()[1]*0.95, 'Projected →',
fontsize=9, color='gray', ha='left')
# Source line
fig.text(0.12, 0.02, 'Source: Company data, [Firm] estimates',
fontsize=9, style='italic', color='gray')
# Save
plt.tight_layout()
plt.savefig('chart_03_revenue_by_product_stacked_area.png',
dpi=300, bbox_inches='tight', facecolor='white')
plt.close()
print("✓ Created: chart_03_revenue_by_product_stacked_area.png")
create_revenue_by_product_chart()def create_revenue_by_geography_chart():
"""Create revenue by geography stacked bar chart"""
years_labels = ['2020', '2021', '2022', '2023', '2024',
'2025E', '2026E', '2027E', '2028E', '2029E']
fig, ax = plt.subplots(figsize=(10, 6))
# Create stacked bar chart
width = 0.6
x = np.arange(len(years_labels))
p1 = ax.bar(x, north_america, width, label='North America', color='#1f77b4')
p2 = ax.bar(x, europe, width, bottom=north_america,
label='Europe', color='#ff7f0e')
p3 = ax.bar(x, asia_pacific, width,
bottom=np.array(north_america) + np.array(europe),
label='Asia-Pacific', color='#2ca02c')
p4 = ax.bar(x, rest_of_world, width,
bottom=np.array(north_america) + np.array(europe) + np.array(asia_pacific),
label='Rest of World', color='#d62728')
# Formatting
ax.set_xlabel('Year', fontsize=12, fontweight='bold')
ax.set_ylabel('Revenue ($M)', fontsize=12, fontweight='bold')
ax.set_title('Figure 4 - Revenue by Geography (2020-2029E)',
fontsize=14, fontweight='bold', pad=20)
ax.set_xticks(x)
ax.set_xticklabels(years_labels, rotation=45, ha='right')
# Legend
ax.legend(loc='upper left', frameon=False, fontsize=10)
# Grid
ax.grid(axis='y', alpha=0.3, linestyle='--')
ax.set_axisbelow(True)
# Remove top and right spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Source line
fig.text(0.12, 0.02, 'Source: Company data, [Firm] estimates',
fontsize=9, style='italic', color='gray')
# Save
plt.tight_layout()
plt.savefig('chart_04_revenue_by_geography_stacked_bar.png',
dpi=300, bbox_inches='tight', facecolor='white')
plt.close()
print("✓ Created: chart_04_revenue_by_geography_stacked_bar.png")
create_revenue_by_geography_chart()def create_dcf_sensitivity_heatmap():
"""Create DCF sensitivity analysis heatmap"""
# Create DataFrame
df = pd.DataFrame(dcf_sensitivity,
index=[f'{w}%' for w in wacc_values],
columns=[f'{g}%' for g in terminal_growth])
fig, ax = plt.subplots(figsize=(8, 6))
# Create heatmap
sns.heatmap(df, annot=True, fmt='d', cmap='RdYlGn',
cbar_kws={'label': 'Price per Share ($)'},
linewidths=0.5, linecolor='white',
ax=ax, vmin=35, vmax=95)
# Formatting
ax.set_xlabel('Terminal Growth Rate', fontsize=12, fontweight='bold')
ax.set_ylabel('WACC', fontsize=12, fontweight='bold')
ax.set_title('Figure 28 - DCF Sensitivity Analysis ($/share)',
fontsize=14, fontweight='bold', pad=20)
# Rotate y-axis labels
plt.yticks(rotation=0)
# Source line
fig.text(0.12, 0.02, 'Source: [Firm] estimates',
fontsize=9, style='italic', color='gray')
# Save
plt.tight_layout()
plt.savefig('chart_28_dcf_sensitivity_heatmap.png',
dpi=300, bbox_inches='tight', facecolor='white')
plt.close()
print("✓ Created: chart_28_dcf_sensitivity_heatmap.png")
create_dcf_sensitivity_heatmap()def create_valuation_football_field():
"""Create valuation football field chart"""
fig, ax = plt.subplots(figsize=(10, 5))
# Create horizontal bars
y_positions = np.arange(len(valuation_methods))
colors = ['#1f77b4', '#ff7f0e', '#2ca02c']
for i, (method, low, high, color) in enumerate(
zip(valuation_methods, valuation_low, valuation_high, colors)):
ax.barh(i, high - low, left=low, height=0.6,
color=color, alpha=0.7, label=method)
# Add value labels at ends
ax.text(low - 1, i, f'${low}', va='center', ha='right', fontsize=10)
ax.text(high + 1, i, f'${high}', va='center', ha='left', fontsize=10)
# Add current price line
ax.axvline(x=current_price, color='red', linestyle='--', linewidth=2,
label=f'Current: ${current_price}', alpha=0.7)
# Add target price line
ax.axvline(x=target_price, color='black', linestyle='-', linewidth=2,
label=f'Target: ${target_price}')
# Formatting
ax.set_yticks(y_positions)
ax.set_yticklabels(valuation_methods, fontsize=11)
ax.set_xlabel('Price Per Share ($)', fontsize=12, fontweight='bold')
ax.set_title('Figure 32 - Valuation Football Field',
fontsize=14, fontweight='bold', pad=20)
# Set x-axis limits
ax.set_xlim(40, 70)
# Remove spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
# Grid
ax.grid(axis='x', alpha=0.3, linestyle='--')
ax.set_axisbelow(True)
# Legend
ax.legend(loc='upper right', frameon=False, fontsize=9)
# Source line
fig.text(0.12, 0.02, 'Source: [Firm] estimates',
fontsize=9, style='italic', color='gray')
# Save
plt.tight_layout()
plt.savefig('chart_32_valuation_football_field.png',
dpi=300, bbox_inches='tight', facecolor='white')
plt.close()
print("✓ Created: chart_32_valuation_football_field.png")
create_valuation_football_field()Complete the 25 REQUIRED charts by creating all remaining charts from the required list. Each chart has a specific purpose in Task 5.
# chart_01: Stock Price Performance (12-24 months)
# - Line chart showing stock price over time vs. market index
# - Used on Page 1 of final report# chart_02: Revenue Growth Trajectory
# chart_10: Gross Margin Evolution
# chart_11: EBITDA Margin Progression
# chart_12: Free Cash Flow Trend
# chart_14: Scenario Comparison (Bull/Base/Bear)# chart_05: Company Overview/Timeline
# chart_06: Key Milestones Timeline
# chart_07: Organizational Structure
# chart_08: Product Portfolio Overview
# chart_09: Customer Segmentation
# chart_15: Market Size Evolution (TAM)
# chart_16: Competitive Positioning Matrix# chart_17: Market Share Breakdown
# chart_18: Competitive Benchmarking# chart_13: Operating Metrics Dashboard# chart_29: DCF Valuation Waterfall
# chart_30: Trading Comps Scatter Plot
# chart_31: Peer Multiples Comparison
# chart_33: Price Target Scenarios
# chart_34: Historical Valuation MultiplesUse consistent formatting across all charts:
Optional: Add 1-10 additional charts from this list for greater visual density:
# chart_19: Customer Acquisition Trends
# chart_20: Unit Economics Evolution
# chart_21: Product Roadmap Timeline
# chart_22: Geographic Expansion Map
# chart_23: R&D Investment Trends
# chart_24: Sales & Marketing Efficiency
# chart_25: Working Capital Trends
# chart_26: Debt Maturity Schedule
# chart_27: Ownership Structure
# chart_35: Analyst Price Target DistributionThese optional charts provide additional visual storytelling and help achieve the "1 chart per 200-300 words" density target in Task 5.
Create a text file documenting all charts:
def create_chart_index():
"""Create index of all charts"""
# 25 REQUIRED CHARTS
required_charts = [
"chart_01_stock_price_performance.png - Stock Price Performance (12-24M)",
"chart_02_revenue_growth_trajectory.png - Revenue Growth Trajectory",
"chart_03_revenue_by_product_stacked_area.png - Revenue by Product [MANDATORY]",
"chart_04_revenue_by_geography_stacked_bar.png - Revenue by Geography [MANDATORY]",
"chart_05_company_overview.png - Company Overview/Timeline",
"chart_06_key_milestones_timeline.png - Key Milestones Timeline",
"chart_07_organizational_structure.png - Organizational Structure",
"chart_08_product_portfolio.png - Product Portfolio Overview",
"chart_09_customer_segmentation.png - Customer Segmentation",
"chart_10_gross_margin_evolution.png - Gross Margin Evolution",
"chart_11_ebitda_margin_progression.png - EBITDA Margin Progression",
"chart_12_free_cash_flow_trend.png - Free Cash Flow Trend",
"chart_13_operating_metrics_dashboard.png - Operating Metrics Dashboard",
"chart_14_scenario_comparison.png - Scenario Comparison (Bull/Base/Bear)",
"chart_15_market_size_evolution.png - Market Size Evolution (TAM)",
"chart_16_competitive_positioning.png - Competitive Positioning Matrix",
"chart_17_market_share.png - Market Share Breakdown",
"chart_18_competitive_benchmarking.png - Competitive Benchmarking",
"chart_28_dcf_sensitivity_heatmap.png - DCF Sensitivity Heatmap [MANDATORY]",
"chart_29_dcf_waterfall.png - DCF Valuation Waterfall",
"chart_30_trading_comps_scatter.png - Trading Comps Scatter Plot",
"chart_31_peer_multiples_comparison.png - Peer Multiples Comparison",
"chart_32_valuation_football_field.png - Valuation Football Field [MANDATORY]",
"chart_33_price_target_scenarios.png - Price Target Scenarios",
"chart_34_historical_valuation_multiples.png - Historical Valuation Multiples",
]
# 10 OPTIONAL CHARTS (for 26-35 range)
optional_charts = [
"chart_19_customer_acquisition_trends.png - Customer Acquisition Trends [OPTIONAL]",
"chart_20_unit_economics_evolution.png - Unit Economics Evolution [OPTIONAL]",
"chart_21_product_roadmap_timeline.png - Product Roadmap Timeline [OPTIONAL]",
"chart_22_geographic_expansion_map.png - Geographic Expansion Map [OPTIONAL]",
"chart_23_rd_investment_trends.png - R&D Investment Trends [OPTIONAL]",
"chart_24_sales_marketing_efficiency.png - Sales & Marketing Efficiency [OPTIONAL]",
"chart_25_working_capital_trends.png - Working Capital Trends [OPTIONAL]",
"chart_26_debt_maturity_schedule.png - Debt Maturity Schedule [OPTIONAL]",
"chart_27_ownership_structure.png - Ownership Structure [OPTIONAL]",
"chart_35_analyst_price_targets.png - Analyst Price Target Distribution [OPTIONAL]",
]
with open('chart_index.txt', 'w') as f:
f.write("CHART INDEX FOR [COMPANY] EQUITY RESEARCH REPORT\n")
f.write("=" * 60 + "\n\n")
f.write("4 MANDATORY CHARTS (Must be present):\n")
f.write("- chart_03: Revenue by Product (Stacked Area) ⭐\n")
f.write("- chart_04: Revenue by Geography (Stacked Bar) ⭐\n")
f.write("- chart_28: DCF Sensitivity (Heatmap) ⭐\n")
f.write("- chart_32: Valuation Football Field ⭐\n\n")
f.write("25 REQUIRED CHARTS:\n")
for chart in required_charts:
f.write(f" {chart}\n")
f.write("\n10 OPTIONAL CHARTS (for 26-35 total):\n")
for chart in optional_charts:
f.write(f" {chart}\n")
f.write("\n" + "=" * 60 + "\n")
f.write("NOTE: Task 5 will embed ALL charts created (25-35) throughout\n")
f.write("the report for visual density (1 chart every 200-300 words).\n")
print("✓ Created: chart_index.txt")
create_chart_index()Run verification checks:
import os
def verify_charts():
"""Verify all charts were created successfully"""
mandatory_charts = [
'chart_03_revenue_by_product_stacked_area.png',
'chart_04_revenue_by_geography_stacked_bar.png',
'chart_28_dcf_sensitivity_heatmap.png',
'chart_32_valuation_football_field.png'
]
print("\n" + "="*60)
print("CHART GENERATION VERIFICATION")
print("="*60)
# Check mandatory charts
print("\n1. MANDATORY CHARTS:")
all_mandatory_present = True
for chart in mandatory_charts:
if os.path.exists(chart):
size = os.path.getsize(chart) / 1024 # KB
print(f" ✓ {chart} ({size:.1f} KB)")
else:
print(f" ✗ MISSING: {chart}")
all_mandatory_present = False
# Count total charts
chart_files = [f for f in os.listdir('.') if f.startswith('chart_') and f.endswith('.png')]
print(f"\n2. TOTAL CHARTS: {len(chart_files)}")
print(f" Target: 25-35 charts")
print(f" Status: {'✓ PASS' if 25 <= len(chart_files) <= 35 else '⚠ WARNING'}")
# Check file sizes (should be > 50KB for 300 DPI)
print("\n3. FILE SIZE CHECK:")
small_files = []
for chart in chart_files[:5]: # Sample first 5
size = os.path.getsize(chart) / 1024
if size < 50:
small_files.append(chart)
print(f" {chart}: {size:.1f} KB")
if small_files:
print(f" ⚠ WARNING: {len(small_files)} files may be low resolution")
else:
print(f" ✓ All sampled files have adequate size")
# Final verdict
print("\n" + "="*60)
if all_mandatory_present and 25 <= len(chart_files) <= 35:
print("✓ VERIFICATION PASSED - Ready for Task 5")
else:
print("✗ VERIFICATION FAILED - Review missing charts")
print("="*60 + "\n")
verify_charts()Line Charts: Time series trends (revenue, margins, stock price)
Stacked Area: Revenue by product ⭐, market size composition
Stacked Bar: Revenue by geography ⭐, quarterly breakdowns
Heatmap: DCF sensitivity ⭐, correlation matrices
Horizontal Bar: Valuation football field ⭐, peer rankings
Waterfall: Revenue bridges, margin analysis, DCF build-up
Scatter/Bubble: Growth vs. valuation, competitive positioning
2×2 Matrix: Competitive positioning, product portfolio
Always use this format:
chart_[NUMBER]_[DESCRIPTION].png
Examples:
chart_01_stock_price_performance.png
chart_03_revenue_by_product_stacked_area.png
chart_28_dcf_sensitivity_heatmap.png
Number charts sequentially based on their position in the report, not creation order.
Problem: Chart looks pixelated
Solution: Ensure dpi=300 in
plt.savefig()
Problem: Labels or titles cut off at edges
Solution: Use bbox_inches='tight' in
plt.savefig()
Problem: Colors don't look professional Solution: Use established palettes like Tableau10 or define custom corporate colors
Problem: Axis labels overlap
Solution: Rotate labels (e.g.,
rotation=45) or reduce font size
Problem: Too much white space around chart
Solution: Use plt.tight_layout() before
saving
A successful chart package should:
Remember: Task 5 will embed ALL charts created (25-35) throughout the report for visual density.
After completing Task 4, deliverables include:
25 REQUIRED Chart Files (Minimum):
10 OPTIONAL Chart Files (For 26-35 Total):
Chart Index (1 text file):
All chart files must be:
Final Step: Package All Charts
Create a zip file containing all chart files and the chart index:
[Company]_Charts_[Date].zip
├── chart_01_stock_price_performance.png
├── chart_02_revenue_growth_trajectory.png
├── chart_03_revenue_by_product_stacked_area.png ⭐
├── chart_04_revenue_by_geography_stacked_bar.png ⭐
├── chart_05_company_overview.png
├── ... (all 25-35 chart files)
├── chart_28_dcf_sensitivity_heatmap.png ⭐
├── chart_32_valuation_football_field.png ⭐
├── chart_34_historical_valuation_multiples.png
└── chart_index.txt
Example:
Tesla_Charts_2024-10-28.zip
Why this matters: Task 5 will embed ALL charts created (25-35) throughout the report. The report requires visual density (1 chart per 200-300 words), so all charts serve a purpose—either for specific analytical sections or for visual storytelling and page density.
After completing Task 4, the zip file will be used for:
The 4 mandatory charts are critical for the valuation and financial analysis sections of the report.