Financial Ratios and Metrics Guide
BusinessMath Quarterly Series
17 min read
Part 7 of 12-Week BusinessMath Series
What You’ll Learn
- How to calculate and interpret profitability ratios (ROA, ROE, ROIC)
- Using efficiency ratios to measure asset utilization
- Assessing liquidity and solvency for financial health
- Applying DuPont analysis to decompose ROE
- Using credit metrics like Altman Z-Score and Piotroski F-Score
The Problem
Analyzing financial statements requires calculating dozens of ratios across five categories: profitability, efficiency, liquidity, solvency, and valuation. Each ratio has a specific formula, interpretation guidelines, and industry benchmarks.Doing this manually is tedious and error-prone. Spreadsheets help, but lack type safety and composability. You need to:
- Calculate ratios consistently across periods
- Track trends over time
- Compare companies on equal footing
- Assess financial health with composite scores
The Solution
BusinessMath provides comprehensive ratio analysis functions that work withIncomeStatement and
BalanceSheet data structures, returning results as
TimeSeries for trend analysis.
Setup: Creating Financial Statements
First, let’s create sample financial statements for a fictional SaaS company “TechCo”:// Define company and periods
let entity = Entity(id: “TECH”, primaryType: .ticker, name: “TechCo Inc.”)
let periods = [
Period.quarter(year: 2025, quarter: 1),
Period.quarter(year: 2025, quarter: 2),
Period.quarter(year: 2025, quarter: 3),
Period.quarter(year: 2025, quarter: 4)
]
// Convenient period references
let q1 = periods[0]
let q2 = periods[1]
let q3 = periods[2]
let q4 = periods[3]
// Create Income Statement
// Revenue: $5M → $6M over the year (20% growth)
let revenueSeries = TimeSeries
(
periods: periods,
values: [5_000_000, 5_300_000, 5_600_000, 6_000_000]
)
let revenueAccount = try Account(
entity: entity,
name: “Subscription Revenue”,
type: .revenue,
timeSeries: revenueSeries
)
// COGS: 30% of revenue
var cogsMetadata = AccountMetadata()
cogsMetadata.category = “COGS”
let cogsSeries = TimeSeries
(
periods: periods,
values: [1_500_000, 1_590_000, 1_680_000, 1_800_000]
)
let cogsAccount = try Account(
entity: entity,
name: “Cost of Goods Sold”,
type: .expense,
timeSeries: cogsSeries,
metadata: cogsMetadata
)
// Operating Expenses: R&D + S&M + G&A
var opexMetadata = AccountMetadata()
opexMetadata.category = “Operating”
let opexSeries = TimeSeries
(
periods: periods,
values: [2_000_000, 2_100_000, 2_150_000, 2_200_000]
)
let opexAccount = try Account(
entity: entity,
name: “Operating Expenses”,
type: .expense,
timeSeries: opexSeries,
metadata: opexMetadata
)
// Interest expense
let interestSeries = TimeSeries
(
periods: periods,
values: [100_000, 95_000, 90_000, 85_000]
)
let interestAccount = try Account(
entity: entity,
name: “Interest Expense”,
type: .expense,
timeSeries: interestSeries
)
let incomeStatement = try IncomeStatement(
entity: entity,
periods: periods,
revenueAccounts: [revenueAccount],
expenseAccounts: [cogsAccount, opexAccount, interestAccount]
)
// Create Balance Sheet
// Current Assets
var currentAssetMetadata = AccountMetadata()
currentAssetMetadata.category = “Current”
let cashSeries = TimeSeries
(
periods: periods,
values: [3_000_000, 3_500_000, 4_000_000, 4_500_000]
)
let cashAccount = try Account(
entity: entity,
name: “Cash”,
type: .asset,
timeSeries: cashSeries,
metadata: currentAssetMetadata
)
let receivablesSeries = TimeSeries
(
periods: periods,
values: [1_200_000, 1_300_000, 1_400_000, 1_500_000]
)
let receivablesAccount = try Account(
entity: entity,
name: “Accounts Receivable”,
type: .asset,
timeSeries: receivablesSeries,
metadata: currentAssetMetadata
)
// Fixed Assets
var fixedAssetMetadata = AccountMetadata()
fixedAssetMetadata.category = “Fixed”
let ppeSeries = TimeSeries
(
periods: periods,
values: [2_000_000, 2_050_000, 2_100_000, 2_150_000]
)
let ppeAccount = try Account(
entity: entity,
name: “Property & Equipment”,
type: .asset,
timeSeries: ppeSeries,
metadata: fixedAssetMetadata
)
// Current Liabilities
var currentLiabilityMetadata = AccountMetadata()
currentLiabilityMetadata.category = “Current”
let payablesSeries = TimeSeries
(
periods: periods,
values: [800_000, 850_000, 900_000, 950_000]
)
let payablesAccount = try Account(
entity: entity,
name: “Accounts Payable”,
type: .liability,
timeSeries: payablesSeries,
metadata: currentLiabilityMetadata
)
// Long-term Debt
var longTermLiabilityMetadata = AccountMetadata()
longTermLiabilityMetadata.category = “Long-term”
let debtSeries = TimeSeries
(
periods: periods,
values: [2_000_000, 1_900_000, 1_800_000, 1_700_000]
)
let debtAccount = try Account(
entity: entity,
name: “Long-term Debt”,
type: .liability,
timeSeries: debtSeries,
metadata: longTermLiabilityMetadata
)
// Equity (balancing to Assets = Liabilities + Equity)
let equitySeries = TimeSeries
(
periods: periods,
values: [3_400_000, 4_100_000, 4_800_000, 5_500_000]
)
let equityAccount = try Account(
entity: entity,
name: “Shareholders Equity”,
type: .equity,
timeSeries: equitySeries
)
let balanceSheet = try BalanceSheet(
entity: entity,
periods: periods,
assetAccounts: [cashAccount, receivablesAccount, ppeAccount],
liabilityAccounts: [payablesAccount, debtAccount],
equityAccounts: [equityAccount]
)
// Market data for valuation metrics
let marketPrice = 45.00 // $45 per share
let sharesOutstanding = 200_000.0 // 200K shares outstanding
// Cash flow statement (for Piotroski F-Score)
let operatingCashFlowSeries = TimeSeries
(
periods: periods,
values: [1_500_000, 1_600_000, 1_700_000, 1_900_000]
)
let cashFlowAccount = try Account(
entity: entity,
name: “Operating Cash Flow”,
type: .operating, // Must use .operating for operating cash flow accounts
timeSeries: operatingCashFlowSeries
)
let cashFlowStatement = try CashFlowStatement(
entity: entity,
periods: periods,
operatingAccounts: [cashFlowAccount],
investingAccounts: [],
financingAccounts: []
)
About TechCo’s Financials:
- Revenue: Growing SaaS company, $5M → $6M quarterly (20% annual growth)
- Gross Margin: 70% (typical for SaaS: low COGS, high operating leverage)
- Balance Sheet: Healthy cash position ($3M → $4.5M), paying down debt ($2M → $1.7M)
- Equity: Growing from retained earnings as company becomes profitable
incomeStatement,
balanceSheet,
cashFlowStatement,
q1-
q4,
periods,
marketPrice, and
sharesOutstanding.
Profitability Ratios
How efficiently does the company generate profits?import BusinessMath
// Get all profitability ratios at once
let profitability = profitabilityRatios(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet
)
print(”=== Profitability Analysis ===”)
print(“Gross Margin: (profitability.grossMargin[q1]!.percent(1))”)
print(“Operating Margin: (profitability.operatingMargin[q1]!.percent(1))”)
print(“Net Margin: (profitability.netMargin[q1]!.percent(1))”)
print(“EBITDA Margin: (profitability.ebitdaMargin[q1]!.percent(1))”)
print(“ROA: (profitability.roa[q1]!.percent(1))”)
print(“ROE: (profitability.roe[q1]!.percent(1))”)
print(“ROIC: (profitability.roic[q1]!.percent(1))”)
Interpretation:
- Gross Margin > 40%: Strong pricing power
- ROA > 5%: Good asset efficiency (varies by industry)
- ROE > 15%: Strong returns for shareholders
- ROIC > WACC: Company creates value
Efficiency Ratios
How effectively does the company use its assets?let efficiency = efficiencyRatios(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet
)
print(”\n=== Efficiency Analysis ===”)
print(“Asset Turnover: (efficiency.assetTurnover[q1]!.number(2))”)
print(“Inventory Turnover: (efficiency.inventoryTurnover![q1]!.number(1))”)
print(“Receivables Turnover: (efficiency.receivablesTurnover![q1]!.number(1))”)
print(“Days Sales Outstanding: (efficiency.daysSalesOutstanding![q1]!.number(1)) days”)
print(“Days Inventory Outstanding: (efficiency.daysInventoryOutstanding![q1]!.number(1)) days”)
print(“Days Payable Outstanding: (efficiency.daysPayableOutstanding![q1]!.number(1)) days”)
// Cash Conversion Cycle
let ccc = efficiency.cashConversionCycle![q1]!
print(“Cash Conversion Cycle: (ccc.number(1)) days”)
Interpretation:
- Higher turnover = more efficient use of assets
- Lower DSO (Days Sales Outstanding) = faster cash collection
- Shorter CCC (Cash Conversion Cycle) = less cash tied up in operations
- Always compare to industry benchmarks
Liquidity Ratios
Can the company meet short-term obligations?print(”\n=== Liquidity Analysis ===”)
print(“Current Ratio: (liquidity.currentRatio[q1]!)”)
print(“Quick Ratio: (liquidity.quickRatio[q1]!)”)
print(“Cash Ratio: (liquidity.cashRatio[q1]!)”)
print(“Working Capital: (liquidity.workingCapital[q1]!.currency(0))”)
// Assess liquidity health
let currentRatio = liquidity.currentRatio[q1]!
if currentRatio < 1.0 {
print(“⚠️ Warning: Current ratio < 1.0 indicates potential liquidity issues”)
} else if currentRatio > 3.0 {
print(“ℹ️ Note: High current ratio may indicate inefficient use of assets”)
} else {
print(“✓ Current ratio in healthy range”)
}
Interpretation:
- Current Ratio > 1.5: Good short-term health
- Quick Ratio > 1.0: Can pay bills without selling inventory
- Cash Ratio > 0.5: Strong
- Too high may indicate poor asset utilization
Solvency Ratios
Can the company meet long-term obligations?let solvency = solvencyRatios(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet
)
print(”\n=== Solvency Analysis ===”)
print(“Debt-to-Equity: (solvency.debtToEquity[q2]!.number(2))”)
print(“Debt-to-Assets: (solvency.debtToAssets[q2]!.number(2))”)
print(“Equity Ratio: (solvency.equityRatio[q2]!.number(2))”)
print(“Interest Coverage: (solvency.interestCoverage![q2]!.number(1))x”)
print(“Debt Service Coverage: (solvency.debtServiceCoverage![q2]!.number(1))x”)
// Assess leverage
let debtToEquity = solvency.debtToEquity[q1]!
if debtToEquity > 2.0 {
print(“⚠️ High leverage - company relies heavily on debt”)
} else if debtToEquity < 0.5 {
print(“ℹ️ Conservative capital structure - may be underlevered”)
} else {
print(“✓ Balanced capital structure”)
}
// Check interest coverage
let interestCoverage = solvency.interestCoverage[q1]!
if interestCoverage < 2.0 {
print(“⚠️ Low interest coverage - may struggle to pay interest”)
} else if interestCoverage > 5.0 {
print(“✓ Strong interest coverage”)
}
Interpretation:
- Lower D/E: Less risky, but may miss growth opportunities
- Higher D/E: More leverage, higher risk and return potential
- Interest Coverage > 3x: Generally safe
- Industry context matters (utilities vs tech)
DuPont Analysis
Decompose ROE to understand its drivers:// 3-Way DuPont Analysis
let dupont = dupontAnalysis(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet
)
print(”\n=== 3-Way DuPont Analysis ===”)
print(“ROE = Net Margin × Asset Turnover × Equity Multiplier\n”)
print(“Net Margin: (dupont.netMargin[q1]!.percent())”)
print(“Asset Turnover: (dupont.assetTurnover[q1]!.number(1))x”)
print(“Equity Multiplier: (dupont.equityMultiplier[q1]!.number(1))x”)
print(“ROE: (dupont.roe[q1]!.percent(1))”)
// Verify the formula
let calculated = dupont.netMargin[q1]! *
dupont.assetTurnover[q1]! *
dupont.equityMultiplier[q1]!
print(”\nVerification: (calculated.percent()) ≈ (dupont.roe[q1]!.percent())”)
ROE can be high due to:
- High Net Margin: Pricing power (luxury goods)
- High Asset Turnover: Efficient operations (retail)
- High Equity Multiplier: Using leverage (banks)
Credit Metrics
Assess bankruptcy risk and fundamental strength:// Altman Z-Score (bankruptcy prediction)
let altmanZ = altmanZScore(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet,
marketPrice: marketPrice,
sharesOutstanding: sharesOutstanding
)
print(”\n=== Altman Z-Score ===”)
print(“Z-Score: (altmanZ[q1]!)”)
let zScore = altmanZ[q1]!
if zScore > 2.99 {
print(“✓ Safe zone - low bankruptcy risk”)
} else if zScore > 1.81 {
print(“⚠️ Grey zone - moderate risk”)
} else {
print(“⚠️ Distress zone - high bankruptcy risk”)
}
// Piotroski F-Score (fundamental strength, 0-9)
let piotroski = piotroskiFScore(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet,
cashFlowStatement: cashFlowStatement
)
print(”\n=== Piotroski F-Score ===”)
print(“F-Score: (Int(piotroski.totalScore)) / 9”)
let fScore = Int(piotroski.totalScore)
if fScore >= 7 {
print(“✓ Strong fundamentals”)
} else if fScore >= 4 {
print(“ℹ️ Moderate fundamentals”)
} else {
print(“⚠️ Weak fundamentals”)
}
Interpretation:
- Altman Z-Score:
- > 3.0: Financially sound
- 1.8-3.0: Watch zone
- < 1.8: High bankruptcy risk
- Piotroski F-Score:
- 8-9: Very strong
- 5-7: Solid
- 0-4: Weak
How It Works
TimeSeries Return Values
All ratio functions returnTimeSeries
, allowing trend analysis:
// Analyze trends across quarters
print(”\n=== Profitability Trends ===”)
print(“Period ROE ROA Net Margin”)
for period in periods {
let roe = profitability.roe[period]!
let roa = profitability.roa[period]!
let margin = profitability.netMargin[period]!
print(”(period.label.padding(toLength: 7, withPad: “ “, startingAt: 0)) (roe.percent(1).paddingLeft(toLength: 8)) (roa.percent(1).paddingLeft(toLength: 8)) (margin.percent(1).paddingLeft(toLength: 12))”)
}
// Calculate quarter-over-quarter growth
let q1_roe = profitability.roe[q1]!
let q2_roe = profitability.roe[q2]!
let qoq_growth = ((q2_roe - q1_roe) / q1_roe)
print(”\nQ2 ROE growth vs Q1: (qoq_growth.percent())”)
Industry Benchmarks
Typical ranges vary by industry:Technology:
- Gross Margin: 60-80%
- ROE: 15-30%
- D/E: 0.1-0.5 (low leverage)
- Asset Turnover: 0.5-1.0
- Gross Margin: 25-40%
- ROE: 15-25%
- D/E: 0.5-1.5
- Asset Turnover: 2.0-4.0 (high)
- Net Margin: 15-25%
- ROE: 10-15%
- D/E: 5.0-10.0 (high leverage)
- Equity Multiplier: 10-20x
Try It Yourself
Click to expand full playground code
import BusinessMath
// Define company and periods
let entity = Entity(id: "TECH", primaryType: .ticker, name: "TechCo Inc.")
let periods = [
Period.quarter(year: 2025, quarter: 1),
Period.quarter(year: 2025, quarter: 2),
Period.quarter(year: 2025, quarter: 3),
Period.quarter(year: 2025, quarter: 4)
]
// Convenient period references
let q1 = periods[0]
let q2 = periods[1]
let q3 = periods[2]
let q4 = periods[3]
// Create Income Statement
// Revenue: $5M → $6M over the year (20% growth)
let revenueSeries = TimeSeries
(
periods: periods,
values: [5_000_000, 5_300_000, 5_600_000, 6_000_000]
)
let revenueAccount = try Account(
entity: entity,
name: "Subscription Revenue",
incomeStatementRole: .serviceRevenue,
timeSeries: revenueSeries
)
// COGS: 30% of revenue
let cogsSeries = TimeSeries
(
periods: periods,
values: [1_500_000, 1_590_000, 1_680_000, 1_800_000]
)
let cogsAccount = try Account(
entity: entity,
name: "Cost of Goods Sold",
incomeStatementRole: .costOfGoodsSold,
timeSeries: cogsSeries
)
// Operating Expenses: R&D + S&M + G&A
let opexSeries = TimeSeries
(
periods: periods,
values: [2_000_000, 2_100_000, 2_150_000, 2_200_000]
)
let opexAccount = try Account(
entity: entity,
name: "Operating Expenses",
incomeStatementRole: .operatingExpenseOther,
timeSeries: opexSeries
)
// Interest expense
let interestSeries = TimeSeries
(
periods: periods,
values: [100_000, 95_000, 90_000, 85_000]
)
let interestAccount = try Account(
entity: entity,
name: "Interest Expense",
incomeStatementRole: .interestExpense,
timeSeries: interestSeries
)
let incomeStatement = try IncomeStatement(
entity: entity,
periods: periods,
accounts: [revenueAccount, cogsAccount, opexAccount, interestAccount]
)
// Create Balance Sheet
// Current Assets
let cashSeries = TimeSeries
(
periods: periods,
values: [3_000_000, 3_500_000, 4_000_000, 4_500_000]
)
let cashAccount = try Account(
entity: entity,
name: "Cash",
balanceSheetRole: .cashAndEquivalents,
timeSeries: cashSeries
)
let receivablesSeries = TimeSeries
(
periods: periods,
values: [1_200_000, 1_300_000, 1_400_000, 1_500_000]
)
let receivablesAccount = try Account(
entity: entity,
name: "Accounts Receivable",
balanceSheetRole: .accountsReceivable, // Required for receivables turnover
timeSeries: receivablesSeries
)
// Inventory (needed for inventory turnover)
let inventorySeries = TimeSeries
(
periods: periods,
values: [500_000, 520_000, 540_000, 560_000]
)
let inventoryAccount = try Account(
entity: entity,
name: "Inventory",
balanceSheetRole: .inventory, // Required for inventory turnover
timeSeries: inventorySeries
)
// Fixed Assets
let ppeSeries = TimeSeries
(
periods: periods,
values: [2_000_000, 2_050_000, 2_100_000, 2_150_000]
)
let ppeAccount = try Account(
entity: entity,
name: "Property & Equipment",
balanceSheetRole: .propertyPlantEquipment,
timeSeries: ppeSeries
)
// Current Liabilities
let payablesSeries = TimeSeries
(
periods: periods,
values: [800_000, 850_000, 900_000, 950_000]
)
let payablesAccount = try Account(
entity: entity,
name: "Accounts Payable",
balanceSheetRole: .accountsPayable, // Required for days payable outstanding
timeSeries: payablesSeries
)
// Long-term Debt
let debtSeries = TimeSeries
(
periods: periods,
values: [2_000_000, 1_900_000, 1_800_000, 1_700_000]
)
let debtAccount = try Account(
entity: entity,
name: "Long-term Debt",
balanceSheetRole: .longTermDebt,
timeSeries: debtSeries
)
// Equity (balancing to Assets = Liabilities + Equity)
// Adjusted for inventory: Assets now include $500K+ inventory each quarter
let equitySeries = TimeSeries
(
periods: periods,
values: [3_900_000, 4_620_000, 5_340_000, 6_060_000]
)
let equityAccount = try Account(
entity: entity,
name: "Shareholders Equity",
balanceSheetRole: .commonStock,
timeSeries: equitySeries
)
let balanceSheet = try BalanceSheet(
entity: entity,
periods: periods,
accounts: [cashAccount, receivablesAccount, inventoryAccount, ppeAccount, payablesAccount, debtAccount, equityAccount]
)
// Market data for valuation metrics
let marketPrice = 45.00 // $45 per share
let sharesOutstanding = 200_000.0 // 200K shares outstanding
// Cash flow statement (for Piotroski F-Score)
let operatingCashFlowSeries = TimeSeries
(
periods: periods,
values: [1_500_000, 1_600_000, 1_700_000, 1_900_000]
)
let cashFlowAccount = try Account(
entity: entity,
name: "Operating Cash Flow",
cashFlowRole: .otherOperatingActivities, // Use cashFlowRole for cash flow accounts
timeSeries: operatingCashFlowSeries
)
let cashFlowStatement = try CashFlowStatement(
entity: entity,
periods: periods,
accounts: [cashFlowAccount]
)
// Get all profitability ratios at once
let profitability = profitabilityRatios(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet
)
print("=== Profitability Analysis ===")
print("Gross Margin: \(profitability.grossMargin[q2]!.percent(1))")
print("Operating Margin: \(profitability.operatingMargin[q2]!.percent(1))")
print("Net Margin: \(profitability.netMargin[q2]!.percent(1))")
print("EBITDA Margin: \(profitability.ebitdaMargin[q2]!.percent(1))")
print("ROA: \(profitability.roa[q2]!.percent(1))")
print("ROE: \(profitability.roe[q2]!.percent(1))")
print("ROIC: \(profitability.roic[q2]!.percent(1))")
let efficiency = efficiencyRatios(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet
)
print("\n=== Efficiency Analysis ===")
print("Asset Turnover: \(efficiency.assetTurnover[q2]!.number(2))")
print("Inventory Turnover: \(efficiency.inventoryTurnover![q2]!.number(1))")
print("Receivables Turnover: \(efficiency.receivablesTurnover![q2]!.number(1))")
print("Days Sales Outstanding: \(efficiency.daysSalesOutstanding![q2]!.number(1)) days")
print("Days Inventory Outstanding: \(efficiency.daysInventoryOutstanding![q2]!.number(1)) days")
print("Days Payable Outstanding: \(efficiency.daysPayableOutstanding![q2]!.number(1)) days")
// Cash Conversion Cycle
let ccc = efficiency.cashConversionCycle![q2]!
print("Cash Conversion Cycle: \(ccc.number(1)) days")
let liquidity = liquidityRatios(balanceSheet: balanceSheet)
print("\n=== Liquidity Analysis ===")
print("Current Ratio: \(liquidity.currentRatio[q2]!.number(1))")
print("Quick Ratio: \(liquidity.quickRatio[q2]!.number(1))")
print("Cash Ratio: \(liquidity.cashRatio[q2]!.number(1))")
print("Working Capital: \(liquidity.workingCapital[q2]!.currency(0))")
// Assess liquidity health
let currentRatio = liquidity.currentRatio[q2]!
if currentRatio < 1.0 {
print("⚠️ Warning: Current ratio < 1.0 indicates potential liquidity issues")
} else if currentRatio > 3.0 {
print("ℹ️ Note: High current ratio may indicate inefficient use of assets")
} else {
print("✓ Current ratio in healthy range")
}
// Calculate solvency ratios using the convenience API
// Principal payments are automatically derived from period-over-period debt reduction
let solvency = solvencyRatios(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet,
debtAccount: debtAccount, // Automatically calculates principal payments
interestAccount: interestAccount // from balance sheet changes
)
print("\n=== Solvency Analysis ===")
print("Debt-to-Equity: \(solvency.debtToEquity[q2]!.number(2))")
print("Debt-to-Assets: \(solvency.debtToAssets[q2]!.number(2))")
print("Equity Ratio: \(solvency.equityRatio[q2]!.number(2))")
print("Interest Coverage: \(solvency.interestCoverage![q2]!.number(1))x")
print("Debt Service Coverage: \(solvency.debtServiceCoverage![q2]!.number(1))x")
// 3-Way DuPont Analysis
let dupont = dupontAnalysis(
incomeStatement: incomeStatement,
balanceSheet: balanceSheet
)
print("\n=== 3-Way DuPont Analysis ===")
print("ROE = Net Margin × Asset Turnover × Equity Multiplier\n")
print("Net Margin: \(dupont.netMargin[q1]!.percent())")
print("Asset Turnover: \(dupont.assetTurnover[q1]!.number(1))x")
print("Equity Multiplier: \(dupont.equityMultiplier[q1]!.number(1))x")
print("ROE: \(dupont.roe[q1]!.percent(1))")
// Verify the formula
let calculated = dupont.netMargin[q1]! *
dupont.assetTurnover[q1]! *
dupont.equityMultiplier[q1]!
print("\nVerification: \(calculated.percent()) ≈ \(dupont.roe[q1]!.percent())")
// Assess leverage
let debtToEquity = solvency.debtToEquity[q2]!
if debtToEquity > 2.0 {
print("⚠️ High leverage - company relies heavily on debt")
} else if debtToEquity < 0.5 {
print("ℹ️ Conservative capital structure - may be underlevered")
} else {
print("✓ Balanced capital structure")
}
// Check interest coverage
let interestCoverage = solvency.interestCoverage?[q2]! ?? 0.0
if interestCoverage < 2.0 {
print("⚠️ Low interest coverage - may struggle to pay interest")
} else if interestCoverage > 5.0 {
print("✓ Strong interest coverage")
}
// Analyze trends across quarters
print("\n=== Profitability Trends ===")
print("Period ROE ROA Net Margin")
for period in periods {
let roe = profitability.roe[period]!
let roa = profitability.roa[period]!
let margin = profitability.netMargin[period]!
print("\(period.label.padding(toLength: 7, withPad: " ", startingAt: 0)) \(roe.percent(1).paddingLeft(toLength: 8)) \(roa.percent(1).paddingLeft(toLength: 8)) \(margin.percent(1).paddingLeft(toLength: 12))")
}
// Calculate quarter-over-quarter growth
let q1_roe = profitability.roe[q1]!
let q2_roe = profitability.roe[q2]!
let qoq_growth = ((q2_roe - q1_roe) / q1_roe)
print("\nQ2 ROE growth vs Q1: \(qoq_growth.percent())")
Modifications to try:
- Compare profitability ratios for two companies
- Track liquidity trends over multiple quarters
- Perform DuPont analysis to identify ROE drivers
Real-World Application
Investment analysts use financial ratios for every stock evaluation:- Profitability screening: ROE > 15%, ROIC > WACC
- Safety checks: Current Ratio > 1.5, Z-Score > 2.99
- Efficiency comparisons: Compare DSO across industry peers
- Valuation: Low P/E + high Piotroski F-Score = potential value
📝 Development Note
During development, we debated whether to return individual ratios (separate functions for each) or composite structs (one function returning all profitability ratios).The composite approach won because real-world analysis requires calculating many related ratios simultaneously. Calling 7 separate functions for profitability analysis was tedious and led to code duplication.
But we kept individual functions available too:
// Composite (most common)
let all = profitabilityRatios(incomeStatement: is, balanceSheet: bs)
// Individual (when you only need one)
let roe = returnOnEquity(incomeStatement: is, balanceSheet: bs)
The lesson: Provide both convenience (composite) and precision (individual). Let users choose based on their needs.
Related Methodology: The Master Plan (Week 3) - Managing API surface area
Next Steps
Coming up next: Risk Analytics (Friday) - VaR, stress testing, and comprehensive risk metrics.Case Study: Week 3 Friday will combine depreciation + TVM + financial ratios for capital equipment decisions.
Series Progress:
- Week: 2/12
- Posts Published: 7/~48
- Topics Covered: Foundation + Analysis Tools (in progress)
- Playgrounds: 6 available
Tagged with: financial-analysis