Mini-project 3: Balance Sheet¶
1) Setup¶
Again we are keeping the importations on top and pringin the data from the past mini projects, Tidy historical pack from mini project 1 and income statment data from project 2
#importations on top for code quality and organization
from pathlib import Path
import numpy as np
import pandas as pd
from IPython.display import display
pd.set_option('display.max_columns', 200)
pd.set_option('display.width', 180)
pd.set_option('display.float_format', lambda x: f'{x:,.2f}')
#Locating the base direction to get CSVs
def locate_base_dir(start: Path) -> Path:
required_files = [
Path('Mini-projects/outputs/unit1_tidy_historical_pack_usdm.csv'),
Path('Mini-projects/outputs/unit1_tidy_historical_audit.csv'),
Path('Mini projects/outputs/mini2_income_statement_full_model.csv'),
]
for candidate in [start, *start.parents]:
if all((candidate / rel_path).exists() for rel_path in required_files):
return candidate
raise FileNotFoundError('Could not locate the Chapter 4 tidy-data inputs from the current working directory.')
BASE_DIR = locate_base_dir(Path.cwd())
UNIT1_PACK_PATH = BASE_DIR / 'Mini-projects' / 'outputs' / 'unit1_tidy_historical_pack_usdm.csv'
UNIT1_AUDIT_PATH = BASE_DIR / 'Mini-projects' / 'outputs' / 'unit1_tidy_historical_audit.csv'
MINI2_PATH = BASE_DIR / 'Mini projects' / 'outputs' / 'mini2_income_statement_full_model.csv'
OUTPUT_DIR = BASE_DIR / 'Mini-projects' / 'outputs'
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
print(f'Base directory: {BASE_DIR}')
print(f'Unit 1 tidy pack: {UNIT1_PACK_PATH}')
print(f'Unit 1 audit: {UNIT1_AUDIT_PATH}')
print(f'Mini Project 2 model: {MINI2_PATH}')
print(f'Chapter 4 outputs will be written to: {OUTPUT_DIR}')
Base directory: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC Unit 1 tidy pack: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini-projects/outputs/unit1_tidy_historical_pack_usdm.csv Unit 1 audit: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini-projects/outputs/unit1_tidy_historical_audit.csv Mini Project 2 model: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini projects/outputs/mini2_income_statement_full_model.csv Chapter 4 outputs will be written to: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini-projects/outputs
2) Getting the CSV loaded and checked¶
Loading the csv and making sure they are not broke and contain all the data we need
#using pd to read the csv
pack = pd.read_csv(UNIT1_PACK_PATH)
audit = pd.read_csv(UNIT1_AUDIT_PATH)
mini2 = pd.read_csv(MINI2_PATH)
#Setting the required columns we are going to use
required_pack_cols = {
'Quarter', 'FiscalYear', 'QuarterNumber', 'Date', 'Cash', 'CashAndSTInvestments',
'AccountsReceivable', 'Inventory', 'AccountsPayable', 'AccruedExpenses',
'DeferredRevenue', 'OtherCurrentAssets', 'OtherCurrentLiabilities',
'TotalCurrentAssets', 'PPENet', 'TotalAssets', 'TotalLiabilities', 'TotalEquity',
'TotalDebt', 'CapEx', 'Dividends', 'SBC', 'DeltaWC_CFSign'
}
required_audit_cols = {'Quarter', 'AuditPass', 'BalanceError_USDm', 'GrossProfitError_USDm'}
required_mini2_cols = {
'Period', 'FiscalYear', 'Quarter', 'Stage', 'Revenue', 'COGS', 'DA',
'InterestExpense', 'NetIncome', 'EBITDA', 'EBIT', 'GrossProfit',
'OperatingIncome', 'EBT', 'TaxExpense'
}
#As mentioned in first class we should keep guardrails of the code and notify if any of the other projects data broke and we are missing something
missing_pack = sorted(required_pack_cols - set(pack.columns))
missing_audit = sorted(required_audit_cols - set(audit.columns))
missing_mini2 = sorted(required_mini2_cols - set(mini2.columns))
if missing_pack:
raise KeyError(f'Missing pack columns: {missing_pack}')
if missing_audit:
raise KeyError(f'Missing audit columns: {missing_audit}')
if missing_mini2:
raise KeyError(f'Missing mini2 columns: {missing_mini2}')
pack = pack.copy()
pack['ReportDate'] = pd.to_datetime(pack['Date'])
pack['PeriodKey'] = pack['FiscalYear'].astype(int).astype(str) + '-Q' + pack['QuarterNumber'].astype(int).astype(str)
mini2_hist = mini2.loc[mini2['Stage'].eq('Historical')].copy()
mini2_hist['PeriodKey'] = mini2_hist['FiscalYear'].astype(int).astype(str) + '-Q' + mini2_hist['Quarter'].astype(int).astype(str)
spine = pack.merge(
mini2_hist[
[
'PeriodKey', 'Stage', 'Revenue', 'COGS', 'GrossProfit', 'OperatingIncome',
'EBIT', 'DA', 'EBITDA', 'InterestExpense', 'EBT', 'TaxExpense', 'NetIncome'
]
],
on='PeriodKey',
how='inner',
suffixes=('_pack', '_mini2'),
)
spine = spine.sort_values(['FiscalYear', 'QuarterNumber']).reset_index(drop=True)
if spine['PeriodKey'].duplicated().any():
duplicates = sorted(spine.loc[spine['PeriodKey'].duplicated(keep=False), 'PeriodKey'].unique().tolist())
raise ValueError(f'Duplicate merged periods found: {duplicates}')
#Asserting all the 40 quarters are loaded
assert len(pack) == 40, f'Expected 40 historical pack rows, found {len(pack)}'
assert len(mini2_hist) == 40, f'Expected 40 historical mini2 rows, found {len(mini2_hist)}'
assert len(spine) == 40, f'Expected 40 merged historical rows, found {len(spine)}'
source_summary = pd.DataFrame(
[
('unit1_tidy_historical_pack_usdm.csv', len(pack), pack['Quarter'].min(), pack['Quarter'].max()),
('unit1_tidy_historical_audit.csv', len(audit), audit['Quarter'].min(), audit['Quarter'].max()),
('mini2_income_statement_full_model.csv (Historical only)', len(mini2_hist), mini2_hist['PeriodKey'].min(), mini2_hist['PeriodKey'].max()),
('Merged Chapter 4 modeling spine', len(spine), spine['Quarter'].min(), spine['Quarter'].max()),
],
columns=['Source', 'Rows', 'FirstPeriod', 'LastPeriod'],
)
audit_exceptions = audit.loc[~audit['AuditPass']].copy()
display(source_summary)
print(f"Inherited Unit 1 audit passes: {int(audit['AuditPass'].sum())} of {len(audit)} quarters")
if audit_exceptions.empty:
print('No inherited Unit 1 audit exceptions were found.')
else:
print('Inherited Unit 1 audit exceptions are shown below. They come from the prior project output and do not stop the Chapter 4 build.')
display(audit_exceptions)
| Source | Rows | FirstPeriod | LastPeriod | |
|---|---|---|---|---|
| 0 | unit1_tidy_historical_pack_usdm.csv | 40 | FY2016 Q4 | FY2026 Q3 |
| 1 | unit1_tidy_historical_audit.csv | 40 | FY2016 Q4 | FY2026 Q3 |
| 2 | mini2_income_statement_full_model.csv (Histori... | 40 | 2016-Q4 | 2026-Q3 |
| 3 | Merged Chapter 4 modeling spine | 40 | FY2016 Q4 | FY2026 Q3 |
Inherited Unit 1 audit passes: 39 of 40 quarters Inherited Unit 1 audit exceptions are shown below. They come from the prior project output and do not stop the Chapter 4 build.
| Quarter | IS_RecordCount | BS_RecordCount | CF_RecordCount | Ratios_RecordCount | BalanceError_USDm | GrossProfitError_USDm | AuditPass | |
|---|---|---|---|---|---|---|---|---|
| 3 | FY2017 Q3 | 1 | 1 | 1 | 1 | 0.00 | 0.60 | False |
3) Helper functions¶
Here we are creating 3 functions:
- flaging the outliers based on z-scores, can help in financial analysis and highlight important quarters to include in the executive summary
- Finding max absolute value, also guide in the executive summary
- Helping in the exportation phase
def robust_outlier_flags(series: pd.Series, threshold: float = 3.5) -> pd.Series:
values = pd.to_numeric(series, errors='coerce')
median = values.dropna().median()
mad = (values.dropna() - median).abs().median()
if pd.isna(median) or pd.isna(mad) or mad == 0:
return pd.Series(False, index=series.index, dtype='bool')
score = 0.6745 * (values - median).abs() / mad
return score.gt(threshold).fillna(False)
def max_abs_period(frame: pd.DataFrame, column: str) -> tuple[str, float]:
series = pd.to_numeric(frame[column], errors='coerce')
idx = series.abs().idxmax()
return str(frame.loc[idx, 'Quarter']), float(series.loc[idx])
def write_schedule_csv(df: pd.DataFrame, file_name: str) -> Path:
output_path = OUTPUT_DIR / file_name
df.to_csv(output_path, index=False)
return output_path
4) Computing BS metrics¶
This is the core of the project where we are going to create the schedules, assemble the balance sheet, check the build and export the results.
quarter_day_count = 365.0 / 4.0
# Creating WC by hand what are we including on it
working_capital = pd.DataFrame({
'Quarter': spine['Quarter'],
'PeriodKey': spine['PeriodKey'],
'ReportDate': spine['ReportDate'].dt.strftime('%Y-%m-%d'),
'Stage': spine['Stage'],
'Revenue': spine['Revenue_mini2'],
'COGS': spine['COGS_mini2'],
'AR_Reported': spine['AccountsReceivable'],
'Inventory_Reported': spine['Inventory'],
'AP_Reported': spine['AccountsPayable'],
'OtherCurrentAssets': spine['OtherCurrentAssets'],
'AccruedExpenses': spine['AccruedExpenses'],
'DeferredRevenue': spine['DeferredRevenue'],
'OtherCurrentLiabilities': spine['OtherCurrentLiabilities'],
})
#Calculating BS based on schedules, using the quarter_day_count to not have to input the numbers
working_capital['ShortTermInvestments'] = spine['CashAndSTInvestments'] - spine['Cash']
working_capital['DSO'] = quarter_day_count * working_capital['AR_Reported'] / working_capital['Revenue'].replace(0.0, np.nan)
working_capital['DIO'] = quarter_day_count * working_capital['Inventory_Reported'] / working_capital['COGS'].replace(0.0, np.nan)
working_capital['DPO'] = quarter_day_count * working_capital['AP_Reported'] / working_capital['COGS'].replace(0.0, np.nan)
working_capital['CCC'] = working_capital['DSO'] + working_capital['DIO'] - working_capital['DPO']
working_capital['AR'] = working_capital['Revenue'] * working_capital['DSO'] / quarter_day_count
working_capital['Inventory'] = working_capital['COGS'] * working_capital['DIO'] / quarter_day_count
working_capital['AP'] = working_capital['COGS'] * working_capital['DPO'] / quarter_day_count
#Current asset residual
working_capital['CurrentAssetResidual'] = (
spine['TotalCurrentAssets']
- spine['Cash']
- working_capital['ShortTermInvestments']
- working_capital['AR']
- working_capital['Inventory']
- working_capital['OtherCurrentAssets']
)
#Basic working capital calculation
working_capital['NWC'] = (
working_capital['AR']
+ working_capital['Inventory']
+ working_capital['OtherCurrentAssets']
+ working_capital['CurrentAssetResidual']
- working_capital['AP']
- working_capital['AccruedExpenses']
- working_capital['DeferredRevenue']
- working_capital['OtherCurrentLiabilities']
)
#final WC calculations
working_capital['Delta_NWC'] = working_capital['NWC'].diff()
working_capital['WC_CashImpact'] = -working_capital['Delta_NWC']
working_capital['ReportedCF_WorkingCapital'] = spine['DeltaWC_CFSign']
working_capital['WC_CashImpact_Gap'] = working_capital['WC_CashImpact'] - working_capital['ReportedCF_WorkingCapital']
working_capital['AR_Error'] = working_capital['AR'] - working_capital['AR_Reported']
working_capital['Inventory_Error'] = working_capital['Inventory'] - working_capital['Inventory_Reported']
working_capital['AP_Error'] = working_capital['AP'] - working_capital['AP_Reported']
working_capital['CCC_OutlierFlag'] = robust_outlier_flags(working_capital['CCC'])
#Moving on to PPE
ppe = pd.DataFrame({
'Quarter': spine['Quarter'],
'PeriodKey': spine['PeriodKey'],
'ReportDate': spine['ReportDate'].dt.strftime('%Y-%m-%d'),
'CapEx': spine['CapEx'],
'Depreciation': spine['DA_mini2'],
'PPE_Reported': spine['PPENet'],
})
#Shifting the PPE so the open is correctly positioned
ppe['PPE_Open'] = ppe['PPE_Reported'].shift(1)
ppe.loc[ppe.index[0], 'PPE_Open'] = max(float(ppe.loc[ppe.index[0], 'PPE_Reported'] - ppe.loc[ppe.index[0], 'CapEx'] + ppe.loc[ppe.index[0], 'Depreciation']), 0.0)
ppe['OtherPPEAdjustments'] = ppe['PPE_Reported'] - ppe['PPE_Open'] - ppe['CapEx'] + ppe['Depreciation']
ppe.loc[ppe.index[0], 'OtherPPEAdjustments'] = 0.0
ppe['PPE_Net'] = ppe['PPE_Open'] + ppe['CapEx'] - ppe['Depreciation'] + ppe['OtherPPEAdjustments']
ppe['PPE_Error'] = ppe['PPE_Net'] - ppe['PPE_Reported']
ppe['PPE_Adjustment_OutlierFlag'] = robust_outlier_flags(ppe['OtherPPEAdjustments'])
#Equity dataframe definition
equity = pd.DataFrame({
'Quarter': spine['Quarter'],
'PeriodKey': spine['PeriodKey'],
'ReportDate': spine['ReportDate'].dt.strftime('%Y-%m-%d'),
'NetIncome': spine['NetIncome_mini2'],
'Dividends': spine['Dividends'],
'StockBasedCompensation': spine['SBC'],
'TotalEquity_Reported': spine['TotalEquity'],
})
#Same process we did with the PPE shift
equity['Equity_Open'] = equity['TotalEquity_Reported'].shift(1)
equity.loc[equity.index[0], 'Equity_Open'] = (
equity.loc[equity.index[0], 'TotalEquity_Reported']
- equity.loc[equity.index[0], 'NetIncome']
+ equity.loc[equity.index[0], 'Dividends']
- equity.loc[equity.index[0], 'StockBasedCompensation']
)
equity['RetainedEarningsLinkage'] = equity['NetIncome'] - equity['Dividends']
equity['CapitalActionsAndOCI'] = (
equity['TotalEquity_Reported']
- equity['Equity_Open']
- equity['RetainedEarningsLinkage']
- equity['StockBasedCompensation']
)
equity.loc[equity.index[0], 'CapitalActionsAndOCI'] = 0.0
equity['TotalEquity'] = (
equity['Equity_Open']
+ equity['RetainedEarningsLinkage']
+ equity['StockBasedCompensation']
+ equity['CapitalActionsAndOCI']
)
equity['TotalEquity_Error'] = equity['TotalEquity'] - equity['TotalEquity_Reported']
equity['Equity_Adjustment_OutlierFlag'] = robust_outlier_flags(equity['CapitalActionsAndOCI'])
#Creating Debt DF
debt = pd.DataFrame({
'Quarter': spine['Quarter'],
'PeriodKey': spine['PeriodKey'],
'ReportDate': spine['ReportDate'].dt.strftime('%Y-%m-%d'),
'Debt_Reported': spine['TotalDebt'],
'InterestExpense_Reported': spine['InterestExpense_mini2'].abs(),
})
debt['Debt_Open'] = debt['Debt_Reported'].shift(1)
debt.loc[debt.index[0], 'Debt_Open'] = debt.loc[debt.index[0], 'Debt_Reported']
debt['Debt_Change'] = debt['Debt_Reported'] - debt['Debt_Open']
debt['Borrowings'] = debt['Debt_Change'].clip(lower=0.0)
debt['Repayments'] = (-debt['Debt_Change']).clip(lower=0.0)
debt['Debt'] = debt['Debt_Open'] + debt['Borrowings'] - debt['Repayments']
debt['Debt_Error'] = debt['Debt'] - debt['Debt_Reported']
debt['AverageDebt'] = (debt['Debt_Open'] + debt['Debt']) / 2.0
fallback_rate = float((debt['InterestExpense_Reported'] / debt['AverageDebt'].replace(0.0, np.nan)).dropna().median())
debt['InterestRate_Implied'] = debt['InterestExpense_Reported'] / debt['AverageDebt'].replace(0.0, np.nan)
debt['InterestRate_Implied'] = debt['InterestRate_Implied'].fillna(fallback_rate).clip(lower=0.0)
debt['InterestExpense_Modeled'] = debt['AverageDebt'] * debt['InterestRate_Implied']
debt['DebtMovement_OutlierFlag'] = robust_outlier_flags(debt['Debt_Change'])
5) Balance sheet DF + CALC¶
Now we are going to tie all the df created in the cell above into the balance sheet
#creating the df
balance_sheet = pd.DataFrame({
'Quarter': spine['Quarter'],
'PeriodKey': spine['PeriodKey'],
'ReportDate': spine['ReportDate'].dt.strftime('%Y-%m-%d'),
'ReportedCash': spine['Cash'],
'ShortTermInvestments': working_capital['ShortTermInvestments'],
'AR': working_capital['AR'],
'Inventory': working_capital['Inventory'],
'OtherCurrentAssets': working_capital['OtherCurrentAssets'],
'CurrentAssetResidual': working_capital['CurrentAssetResidual'],
'PPE_Net': ppe['PPE_Net'],
'AP': working_capital['AP'],
'AccruedExpenses': working_capital['AccruedExpenses'],
'DeferredRevenue': working_capital['DeferredRevenue'],
'OtherCurrentLiabilities': working_capital['OtherCurrentLiabilities'],
'Debt': debt['Debt'],
'TotalEquity': equity['TotalEquity'],
'ReportedTotalAssets': spine['TotalAssets'],
'ReportedTotalLiabilities': spine['TotalLiabilities'],
'ReportedTotalEquity': spine['TotalEquity'],
})
#quantify the other
balance_sheet['OtherAssetResidual'] = (
balance_sheet['ReportedTotalAssets']
- balance_sheet['ReportedCash']
- balance_sheet['ShortTermInvestments']
- balance_sheet['AR']
- balance_sheet['Inventory']
- balance_sheet['OtherCurrentAssets']
- balance_sheet['CurrentAssetResidual']
- balance_sheet['PPE_Net']
)
#quantify the other again
balance_sheet['OtherLiabilityResidual'] = (
balance_sheet['ReportedTotalLiabilities']
- balance_sheet['AP']
- balance_sheet['AccruedExpenses']
- balance_sheet['DeferredRevenue']
- balance_sheet['OtherCurrentLiabilities']
- balance_sheet['Debt']
)
#quantify the TA excluding cash
balance_sheet['TotalAssets_exCash'] = (
balance_sheet['ShortTermInvestments']
+ balance_sheet['AR']
+ balance_sheet['Inventory']
+ balance_sheet['OtherCurrentAssets']
+ balance_sheet['CurrentAssetResidual']
+ balance_sheet['PPE_Net']
+ balance_sheet['OtherAssetResidual']
)
#quantify TL
balance_sheet['TotalLiabilities'] = (
balance_sheet['AP']
+ balance_sheet['AccruedExpenses']
+ balance_sheet['DeferredRevenue']
+ balance_sheet['OtherCurrentLiabilities']
+ balance_sheet['Debt']
+ balance_sheet['OtherLiabilityResidual']
)
balance_sheet['Cash_Reconciled'] = balance_sheet['TotalLiabilities'] + balance_sheet['TotalEquity'] - balance_sheet['TotalAssets_exCash']
balance_sheet['TotalAssets'] = balance_sheet['TotalAssets_exCash'] + balance_sheet['Cash_Reconciled']
balance_sheet['CashPlugGap'] = balance_sheet['Cash_Reconciled'] - balance_sheet['ReportedCash']
balance_sheet['AssetsGap'] = balance_sheet['TotalAssets'] - balance_sheet['ReportedTotalAssets']
balance_sheet['LiabilitiesGap'] = balance_sheet['TotalLiabilities'] - balance_sheet['ReportedTotalLiabilities']
balance_sheet['EquityGap'] = balance_sheet['TotalEquity'] - balance_sheet['ReportedTotalEquity']
balance_sheet['BalanceError'] = balance_sheet['TotalAssets'] - balance_sheet['TotalLiabilities'] - balance_sheet['TotalEquity']
#Balance check (A = L + E)
balance_check = pd.DataFrame({
'Quarter': balance_sheet['Quarter'],
'PeriodKey': balance_sheet['PeriodKey'],
'Cash_Reconciled': balance_sheet['Cash_Reconciled'],
'ReportedCash': balance_sheet['ReportedCash'],
'CashPlugGap': balance_sheet['CashPlugGap'],
'BalanceError': balance_sheet['BalanceError'],
'AssetsGap': balance_sheet['AssetsGap'],
'LiabilitiesGap': balance_sheet['LiabilitiesGap'],
'EquityGap': balance_sheet['EquityGap'],
'CCC': working_capital['CCC'],
'WC_CashImpact_Gap': working_capital['WC_CashImpact_Gap'],
'PPE_OtherAdjustments': ppe['OtherPPEAdjustments'],
'CapitalActionsAndOCI': equity['CapitalActionsAndOCI'],
'Debt_Change': debt['Debt_Change'],
})
#Run all the checks and display them to ensure correctness
balance_check['NegativeCashFlag'] = balance_sheet['Cash_Reconciled'] < 0.0
balance_check['NegativeARFlag'] = balance_sheet['AR'] < 0.0
balance_check['NegativeInventoryFlag'] = balance_sheet['Inventory'] < 0.0
balance_check['NegativeAPFlag'] = balance_sheet['AP'] < 0.0
balance_check['NegativePPEFlag'] = balance_sheet['PPE_Net'] < 0.0
balance_check['NegativeDebtFlag'] = balance_sheet['Debt'] < 0.0
balance_check['NegativeTotalEquityFlag'] = balance_sheet['TotalEquity'] < 0.0
balance_check['CCC_OutlierFlag'] = working_capital['CCC_OutlierFlag'].astype('bool')
balance_check['PPE_Adjustment_OutlierFlag'] = ppe['PPE_Adjustment_OutlierFlag'].astype('bool')
balance_check['Equity_Adjustment_OutlierFlag'] = equity['Equity_Adjustment_OutlierFlag'].astype('bool')
balance_check['DebtMovement_OutlierFlag'] = debt['DebtMovement_OutlierFlag'].astype('bool')
balance_check['CashPlugMismatchFlag'] = balance_check['CashPlugGap'].abs().gt(1e-2)
balance_check['BalanceErrorFlag'] = balance_check['BalanceError'].abs().gt(1e-2)
flag_cols = [col for col in balance_check.columns if col.endswith('Flag')]
balance_check['NegativeBalanceCount'] = balance_check[[col for col in flag_cols if col.startswith('Negative')]].sum(axis=1)
balance_check['OutlierCount'] = balance_check[[col for col in flag_cols if 'Outlier' in col]].sum(axis=1)
balance_check['FlagCount'] = balance_check[flag_cols].sum(axis=1)
working_capital_q = working_capital.set_index('Quarter')
ppe_q = ppe.set_index('Quarter')
equity_q = equity.set_index('Quarter')
debt_q = debt.set_index('Quarter')
balance_sheet_q = balance_sheet.set_index('Quarter')
balance_check_q = balance_check.set_index('Quarter')
#Outputs fro control pupose, using the function we defined in section 2
output_files = {
'working_capital_csv': write_schedule_csv(working_capital, 'chapter4_working_capital_schedule_usdm.csv'),
'ppe_csv': write_schedule_csv(ppe, 'chapter4_ppe_schedule_usdm.csv'),
'equity_csv': write_schedule_csv(equity, 'chapter4_equity_schedule_usdm.csv'),
'debt_csv': write_schedule_csv(debt, 'chapter4_debt_schedule_usdm.csv'),
'balance_sheet_csv': write_schedule_csv(balance_sheet, 'chapter4_balance_sheet_usdm.csv'),
'balance_check_csv': write_schedule_csv(balance_check, 'chapter4_balance_check_report.csv'),
}
print('Schedules built and Chapter 4 outputs exported.')
print(f"Max abs balance error: {balance_check['BalanceError'].abs().max():,.6f}")
print(f"Max abs cash plug gap: {balance_check['CashPlugGap'].abs().max():,.6f}")
Schedules built and Chapter 4 outputs exported. Max abs balance error: 0.000000 Max abs cash plug gap: 0.000000
6) WC driver view¶
wc_driver_view = working_capital_q[
[
'Revenue', 'COGS', 'DSO', 'DIO', 'DPO', 'CCC', 'AR',
'Inventory', 'AP', 'NWC', 'WC_CashImpact', 'ReportedCF_WorkingCapital',
'WC_CashImpact_Gap'
]
]
display(wc_driver_view)
#median CCC to use in the executive summary
median_ccc = working_capital_q["CCC"].median()
display(median_ccc)
| Revenue | COGS | DSO | DIO | DPO | CCC | AR | Inventory | AP | NWC | WC_CashImpact | ReportedCF_WorkingCapital | WC_CashImpact_Gap | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Quarter | |||||||||||||
| FY2016 Q4 | 1,871.00 | 857.00 | 25.19 | 119.79 | 16.08 | 128.90 | 516.50 | 1,125.00 | 151.00 | 1,118.80 | NaN | -153.00 | NaN |
| FY2017 Q1 | 1,552.00 | 657.60 | 19.87 | 172.34 | 26.64 | 165.57 | 338.00 | 1,242.00 | 192.00 | 961.00 | 157.80 | 81.00 | 76.80 |
| FY2017 Q2 | 1,821.00 | 866.40 | 24.55 | 123.54 | 16.75 | 131.35 | 490.00 | 1,173.00 | 159.00 | 1,122.00 | -161.00 | -244.00 | 83.00 |
| FY2017 Q3 | 1,714.00 | 731.40 | 15.17 | 122.76 | 19.71 | 118.23 | 285.00 | 984.00 | 158.00 | 758.00 | 364.00 | 391.00 | -27.00 |
| FY2017 Q4 | 1,565.80 | 746.70 | 26.24 | 96.72 | 18.05 | 104.91 | 450.20 | 791.50 | 147.70 | 706.40 | 51.60 | 5.60 | 46.00 |
| FY2018 Q1 | 1,347.10 | 495.90 | 18.91 | 158.23 | 29.61 | 147.53 | 279.20 | 859.90 | 160.90 | 602.00 | 104.40 | 182.50 | -78.10 |
| FY2018 Q2 | 1,664.20 | 668.40 | 25.79 | 118.04 | 23.59 | 120.23 | 470.30 | 864.60 | 172.80 | 746.10 | -144.10 | -136.20 | -7.90 |
| FY2018 Q3 | 1,641.80 | 645.60 | 16.41 | 116.66 | 26.05 | 107.02 | 295.20 | 825.40 | 184.30 | 486.90 | 259.20 | 479.20 | -220.00 |
| FY2018 Q4 | 1,529.20 | 620.70 | 25.15 | 111.92 | 24.35 | 112.72 | 421.40 | 761.30 | 165.60 | 549.70 | -62.80 | -236.50 | 173.70 |
| FY2019 Q1 | 1,390.60 | 494.90 | 17.06 | 164.10 | 37.37 | 143.79 | 260.00 | 890.00 | 202.70 | 581.50 | -31.80 | 15.40 | -47.20 |
| FY2019 Q2 | 1,690.90 | 661.60 | 23.33 | 137.18 | 27.87 | 132.63 | 432.30 | 994.60 | 202.10 | 898.60 | -317.10 | -293.00 | -24.10 |
| FY2019 Q3 | 1,725.80 | 666.30 | 16.07 | 125.24 | 23.16 | 118.16 | 304.00 | 914.50 | 169.10 | 718.20 | 180.40 | 234.80 | -54.40 |
| FY2019 Q4 | 1,505.70 | 604.20 | 24.13 | 123.51 | 30.55 | 117.08 | 398.10 | 817.80 | 202.30 | 666.00 | 52.20 | -34.20 | 86.40 |
| FY2020 Q1 | 1,428.80 | 508.00 | 18.57 | 177.58 | 63.14 | 133.01 | 290.70 | 988.60 | 351.50 | 720.50 | -54.50 | -0.70 | -53.80 |
| FY2020 Q2 | 1,706.20 | 657.20 | 25.84 | 140.58 | 48.64 | 117.79 | 483.20 | 1,012.50 | 350.30 | 820.60 | -100.10 | -274.00 | 173.90 |
| FY2020 Q3 | 1,750.70 | 661.60 | 18.21 | 124.77 | 36.91 | 106.07 | 349.40 | 904.60 | 267.60 | 570.60 | 250.00 | 246.90 | 3.10 |
| FY2020 Q4 | 1,274.10 | 679.70 | 19.85 | 98.84 | 33.13 | 85.55 | 277.10 | 736.20 | 246.80 | 402.50 | 168.10 | 101.70 | 66.40 |
| FY2021 Q1 | 487.50 | 138.80 | 20.35 | 508.32 | 94.80 | 433.86 | 108.70 | 773.20 | 144.20 | 419.40 | -16.90 | 59.90 | -76.80 |
| FY2021 Q2 | 1,193.50 | 394.10 | 27.07 | 205.38 | 65.87 | 166.57 | 354.00 | 887.00 | 284.50 | 385.80 | 33.60 | 15.70 | 17.90 |
| FY2021 Q3 | 1,432.80 | 502.40 | 23.79 | 157.29 | 60.85 | 120.24 | 373.60 | 866.00 | 335.00 | 265.70 | 120.10 | 155.20 | -35.10 |
| FY2021 Q4 | 1,287.00 | 504.10 | 32.01 | 137.39 | 64.42 | 104.98 | 451.50 | 759.00 | 355.90 | 284.50 | -18.80 | -154.90 | 136.10 |
| FY2022 Q1 | 1,376.30 | 408.20 | 24.35 | 179.50 | 82.78 | 121.07 | 367.20 | 803.00 | 370.30 | 292.40 | -7.90 | -15.20 | 7.30 |
| FY2022 Q2 | 1,504.10 | 488.90 | 25.44 | 173.24 | 84.25 | 114.43 | 419.30 | 928.20 | 451.40 | 310.50 | -18.10 | -52.40 | 34.30 |
| FY2022 Q3 | 1,815.40 | 617.30 | 20.64 | 137.34 | 69.89 | 88.09 | 410.70 | 929.10 | 472.80 | 196.30 | 114.20 | 61.80 | 52.40 |
| FY2022 Q4 | 1,522.70 | 556.60 | 24.29 | 160.22 | 73.56 | 110.95 | 405.40 | 977.30 | 448.70 | 307.60 | -111.30 | -163.80 | 52.50 |
| FY2023 Q1 | 1,490.60 | 489.20 | 21.45 | 219.77 | 104.85 | 136.37 | 350.40 | 1,178.20 | 562.10 | 489.50 | -181.90 | -180.90 | -1.00 |
| FY2023 Q2 | 1,579.90 | 556.80 | 28.28 | 206.72 | 81.61 | 153.39 | 489.60 | 1,261.40 | 498.00 | 776.20 | -286.70 | -289.90 | 3.20 |
| FY2023 Q3 | 1,832.30 | 641.60 | 21.12 | 176.13 | 66.56 | 130.68 | 424.00 | 1,238.40 | 468.00 | 697.60 | 78.60 | 133.80 | -55.20 |
| FY2023 Q4 | 1,540.80 | 590.20 | 26.51 | 165.63 | 57.45 | 134.69 | 447.70 | 1,071.30 | 371.60 | 693.60 | 4.00 | -87.60 | 91.60 |
| FY2024 Q1 | 1,496.50 | 464.50 | 21.09 | 233.34 | 88.09 | 166.34 | 345.80 | 1,187.80 | 448.40 | 554.90 | 138.70 | 56.60 | 82.10 |
| FY2024 Q2 | 1,633.00 | 562.90 | 25.77 | 193.77 | 74.59 | 144.95 | 461.10 | 1,195.30 | 460.10 | 776.70 | -221.80 | -182.20 | -39.60 |
| FY2024 Q3 | 1,934.00 | 648.00 | 19.06 | 148.58 | 57.99 | 109.64 | 403.90 | 1,055.10 | 411.80 | 559.00 | 217.70 | 274.00 | -56.30 |
| FY2024 Q4 | 1,567.90 | 524.20 | 25.99 | 157.05 | 57.83 | 125.21 | 446.50 | 902.20 | 332.20 | 562.20 | -3.20 | -33.40 | 30.20 |
| FY2025 Q1 | 1,512.20 | 446.40 | 22.44 | 212.41 | 97.67 | 137.17 | 371.80 | 1,039.10 | 477.80 | 558.60 | 3.60 | 16.30 | -12.70 |
| FY2025 Q2 | 1,726.00 | 570.30 | 27.38 | 180.47 | 79.31 | 128.53 | 517.90 | 1,127.90 | 495.70 | 685.70 | -127.10 | -146.40 | 19.30 |
| FY2025 Q3 | 2,143.50 | 677.40 | 18.53 | 134.52 | 65.94 | 87.11 | 435.20 | 998.60 | 489.50 | 451.20 | 234.50 | 335.10 | -100.60 |
| FY2025 Q4 | 1,697.30 | 532.00 | 24.70 | 162.88 | 74.78 | 112.80 | 459.50 | 949.60 | 436.00 | 494.60 | -43.40 | -3.10 | -40.30 |
| FY2026 Q1 | 1,719.10 | 476.80 | 21.05 | 233.90 | 116.57 | 138.39 | 396.60 | 1,222.20 | 609.10 | 598.20 | -103.60 | -117.30 | 13.70 |
| FY2026 Q2 | 2,010.70 | 644.30 | 23.81 | 178.63 | 75.44 | 127.00 | 524.60 | 1,261.30 | 532.70 | 567.30 | 30.90 | -262.50 | 293.40 |
| FY2026 Q3 | 2,406.00 | 724.30 | 17.47 | 144.81 | 68.50 | 93.78 | 460.70 | 1,149.40 | 543.70 | 281.20 | 286.10 | 379.80 | -93.70 |
123.14045795070761
wc_support_view = working_capital_q[
[
'AR_Reported', 'AR_Error', 'Inventory_Reported', 'Inventory_Error',
'AP_Reported', 'AP_Error', 'OtherCurrentAssets', 'CurrentAssetResidual',
'AccruedExpenses', 'DeferredRevenue', 'OtherCurrentLiabilities',
'CCC_OutlierFlag'
]
]
display(wc_support_view.head(12))
wc_summary = working_capital[['DSO', 'DIO', 'DPO', 'CCC', 'WC_CashImpact_Gap']].describe().T
print(f'CCC outlier quarters: {int(working_capital["CCC_OutlierFlag"].sum())}')
display(wc_summary)
| AR_Reported | AR_Error | Inventory_Reported | Inventory_Error | AP_Reported | AP_Error | OtherCurrentAssets | CurrentAssetResidual | AccruedExpenses | DeferredRevenue | OtherCurrentLiabilities | CCC_OutlierFlag | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Quarter | ||||||||||||
| FY2016 Q4 | 516.50 | 0.00 | 1,125.00 | 0.00 | 151.00 | 0.00 | 90.20 | 235.60 | 581.20 | 50.10 | 66.20 | False |
| FY2017 Q1 | 338.00 | 0.00 | 1,242.00 | 0.00 | 192.00 | 0.00 | 97.00 | 249.00 | 591.00 | 48.00 | 134.00 | False |
| FY2017 Q2 | 490.00 | 0.00 | 1,173.00 | 0.00 | 159.00 | -0.00 | 81.00 | 267.00 | 565.00 | 38.00 | 127.00 | False |
| FY2017 Q3 | 285.00 | 0.00 | 984.00 | 0.00 | 158.00 | 0.00 | 130.00 | 254.00 | 609.00 | 34.00 | 94.00 | False |
| FY2017 Q4 | 450.20 | 0.00 | 791.50 | 0.00 | 147.70 | 0.00 | 82.30 | 277.50 | 564.60 | 29.70 | 153.10 | False |
| FY2018 Q1 | 279.20 | 0.00 | 859.90 | 0.00 | 160.90 | 0.00 | 90.30 | 286.40 | 587.40 | 30.60 | 134.90 | False |
| FY2018 Q2 | 470.30 | 0.00 | 864.60 | 0.00 | 172.80 | 0.00 | 81.80 | 289.00 | 610.10 | 33.10 | 143.60 | False |
| FY2018 Q3 | 295.20 | 0.00 | 825.40 | 0.00 | 184.30 | 0.00 | 73.70 | 300.90 | 659.60 | 33.90 | 130.50 | False |
| FY2018 Q4 | 421.40 | 0.00 | 761.30 | 0.00 | 165.60 | 0.00 | 78.90 | 282.80 | 668.30 | 30.40 | 130.40 | False |
| FY2019 Q1 | 260.00 | 0.00 | 890.00 | 0.00 | 202.70 | 0.00 | 104.30 | 275.80 | 631.20 | 25.50 | 89.20 | False |
| FY2019 Q2 | 432.30 | 0.00 | 994.60 | 0.00 | 202.10 | 0.00 | 81.80 | 312.20 | 649.60 | 19.00 | 51.60 | False |
| FY2019 Q3 | 304.00 | 0.00 | 914.50 | 0.00 | 169.10 | -0.00 | 93.90 | 321.00 | 668.40 | 20.10 | 57.60 | False |
CCC outlier quarters: 1
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| DSO | 40.00 | 22.57 | 3.85 | 15.17 | 19.65 | 23.56 | 25.52 | 32.01 |
| DIO | 40.00 | 165.98 | 65.89 | 96.72 | 125.12 | 157.76 | 178.85 | 508.32 |
| DPO | 40.00 | 56.63 | 27.47 | 16.08 | 29.17 | 61.99 | 74.95 | 116.57 |
| CCC | 40.00 | 131.92 | 52.95 | 85.55 | 112.28 | 123.14 | 136.57 | 433.86 |
| WC_CashImpact_Gap | 39.00 | 13.32 | 90.04 | -220.00 | -43.75 | 3.20 | 59.45 | 293.40 |
7) Roll foward section - PPE¶
PP&E is reconstructed as opening (reason for the shift 1) net PP&E plus CapEx minus depreciation plus any residual adjustment required to tie back to the reported closing balance. We can use the tidy DB from module 1
ppe_view = ppe_q[
[
'PPE_Open', 'CapEx', 'Depreciation', 'OtherPPEAdjustments',
'PPE_Net', 'PPE_Reported', 'PPE_Error', 'PPE_Adjustment_OutlierFlag'
]
]
display(ppe_view)
#For usage in the executive summary
largest_ppe_residual_period = ppe.loc[ppe['OtherPPEAdjustments'].abs().idxmax(), 'Quarter']
print(f'Largest PP&E residual quarter: {largest_ppe_residual_period}')
print(f"Largest abs PP&E residual: {ppe['OtherPPEAdjustments'].abs().max():,.2f} USDm")
| PPE_Open | CapEx | Depreciation | OtherPPEAdjustments | PPE_Net | PPE_Reported | PPE_Error | PPE_Adjustment_OutlierFlag | |
|---|---|---|---|---|---|---|---|---|
| Quarter | ||||||||
| FY2016 Q4 | 1,497.00 | 93.00 | 7.00 | 0.00 | 1,583.00 | 1,583.00 | 0.00 | False |
| FY2017 Q1 | 1,583.00 | 78.00 | 6.00 | -90.00 | 1,565.00 | 1,565.00 | 0.00 | False |
| FY2017 Q2 | 1,565.00 | 87.00 | 76.00 | -12.00 | 1,564.00 | 1,564.00 | 0.00 | False |
| FY2017 Q3 | 1,564.00 | 60.00 | 6.00 | -104.00 | 1,514.00 | 1,514.00 | 0.00 | False |
| FY2017 Q4 | 1,514.00 | 59.00 | 75.50 | -181.50 | 1,316.00 | 1,316.00 | 0.00 | True |
| FY2018 Q1 | 1,316.00 | 41.90 | 72.90 | -11.70 | 1,273.30 | 1,273.30 | 0.00 | False |
| FY2018 Q2 | 1,273.30 | 32.80 | 73.80 | 8.20 | 1,240.50 | 1,240.50 | 0.00 | False |
| FY2018 Q3 | 1,240.50 | 48.30 | 72.70 | -0.20 | 1,215.90 | 1,215.90 | 0.00 | False |
| FY2018 Q4 | 1,215.90 | 38.60 | 75.80 | 7.60 | 1,186.30 | 1,186.30 | 0.00 | False |
| FY2019 Q1 | 1,186.30 | 42.30 | 70.30 | -16.60 | 1,141.70 | 1,141.70 | 0.00 | False |
| FY2019 Q2 | 1,141.70 | 50.80 | 69.70 | 7.70 | 1,130.50 | 1,130.50 | 0.00 | False |
| FY2019 Q3 | 1,130.50 | 56.10 | 72.00 | -35.30 | 1,079.30 | 1,079.30 | 0.00 | False |
| FY2019 Q4 | 1,079.30 | 48.50 | 69.30 | -19.30 | 1,039.20 | 1,039.20 | 0.00 | False |
| FY2020 Q1 | 1,039.20 | 49.40 | 66.20 | 1,380.40 | 2,402.80 | 2,402.80 | 0.00 | True |
| FY2020 Q2 | 2,402.80 | 81.20 | 66.60 | 160.70 | 2,578.10 | 2,578.10 | 0.00 | True |
| FY2020 Q3 | 2,578.10 | 85.40 | 68.20 | 6.30 | 2,601.60 | 2,601.60 | 0.00 | False |
| FY2020 Q4 | 2,601.60 | 54.30 | 68.50 | -96.30 | 2,491.10 | 2,491.10 | 0.00 | False |
| FY2021 Q1 | 2,491.10 | 21.30 | 63.70 | -38.80 | 2,409.90 | 2,409.90 | 0.00 | False |
| FY2021 Q2 | 2,409.90 | 32.60 | 61.50 | -53.20 | 2,327.80 | 2,327.80 | 0.00 | False |
| FY2021 Q3 | 2,327.80 | 26.90 | 60.30 | 131.60 | 2,426.00 | 2,426.00 | 0.00 | True |
| FY2021 Q4 | 2,426.00 | 27.00 | 62.10 | -137.40 | 2,253.50 | 2,253.50 | 0.00 | False |
| FY2022 Q1 | 2,253.50 | 28.20 | 57.20 | -68.60 | 2,155.90 | 2,155.90 | 0.00 | False |
| FY2022 Q2 | 2,155.90 | 35.20 | 55.90 | -14.90 | 2,120.30 | 2,120.30 | -0.00 | False |
| FY2022 Q3 | 2,120.30 | 50.20 | 56.20 | -17.30 | 2,097.00 | 2,097.00 | 0.00 | False |
| FY2022 Q4 | 2,097.00 | 53.30 | 60.40 | -9.10 | 2,080.80 | 2,080.80 | 0.00 | False |
| FY2023 Q1 | 2,080.80 | 39.40 | 54.80 | -79.50 | 1,985.90 | 1,985.90 | 0.00 | False |
| FY2023 Q2 | 1,985.90 | 44.50 | 53.30 | -61.30 | 1,915.80 | 1,915.80 | 0.00 | False |
| FY2023 Q3 | 1,915.80 | 72.00 | 55.20 | 87.90 | 2,020.50 | 2,020.50 | 0.00 | False |
| FY2023 Q4 | 2,020.50 | 61.60 | 57.20 | 64.60 | 2,089.50 | 2,089.50 | 0.00 | False |
| FY2024 Q1 | 2,089.50 | 39.60 | 58.30 | -34.20 | 2,036.60 | 2,036.60 | -0.00 | False |
| FY2024 Q2 | 2,036.60 | 42.80 | 58.50 | -57.10 | 1,963.80 | 1,963.80 | 0.00 | False |
| FY2024 Q3 | 1,963.80 | 42.50 | 56.20 | 0.90 | 1,951.00 | 1,951.00 | 0.00 | False |
| FY2024 Q4 | 1,951.00 | 39.90 | 56.00 | -69.90 | 1,865.00 | 1,865.00 | 0.00 | False |
| FY2025 Q1 | 1,865.00 | 33.40 | 54.40 | 1.30 | 1,845.30 | 1,845.30 | 0.00 | False |
| FY2025 Q2 | 1,845.30 | 41.70 | 55.90 | 14.90 | 1,846.00 | 1,846.00 | 0.00 | False |
| FY2025 Q3 | 1,846.00 | 61.20 | 54.30 | -3.60 | 1,849.30 | 1,849.30 | 0.00 | False |
| FY2025 Q4 | 1,849.30 | 79.90 | 55.00 | -14.70 | 1,859.50 | 1,859.50 | 0.00 | False |
| FY2026 Q1 | 1,859.50 | 187.30 | 55.50 | 114.20 | 2,105.50 | 2,105.50 | 0.00 | False |
| FY2026 Q2 | 2,105.50 | 93.80 | 57.00 | 47.30 | 2,189.60 | 2,189.60 | 0.00 | False |
| FY2026 Q3 | 2,189.60 | 75.60 | 59.70 | 6.00 | 2,211.50 | 2,211.50 | 0.00 | False |
Largest PP&E residual quarter: FY2020 Q1 Largest abs PP&E residual: 1,380.40 USDm
7) Roll foward section - Equity¶
Same logic here, rolling foward Equity
equity_view = equity_q[
[
'PeriodKey', 'Equity_Open', 'NetIncome', 'Dividends',
'StockBasedCompensation', 'RetainedEarningsLinkage',
'CapitalActionsAndOCI', 'TotalEquity', 'TotalEquity_Reported',
'TotalEquity_Error', 'Equity_Adjustment_OutlierFlag'
]
]
display(equity_view.head(12))
print(f'Equity residual outlier quarters: {int(equity["Equity_Adjustment_OutlierFlag"].sum())}')
print(f"Largest abs capital-actions / OCI residual: {equity['CapitalActionsAndOCI'].abs().max():,.2f} USDm")
| PeriodKey | Equity_Open | NetIncome | Dividends | StockBasedCompensation | RetainedEarningsLinkage | CapitalActionsAndOCI | TotalEquity | TotalEquity_Reported | TotalEquity_Error | Equity_Adjustment_OutlierFlag | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Quarter | |||||||||||
| FY2016 Q4 | 2016-Q4 | 3,727.00 | 41.00 | 42.00 | 18.00 | -1.00 | 0.00 | 3,744.00 | 3,744.00 | 0.00 | False |
| FY2017 Q1 | 2017-Q1 | 3,744.00 | -33.20 | 41.40 | 18.00 | -74.60 | -121.40 | 3,566.00 | 3,566.00 | 0.00 | False |
| FY2017 Q2 | 2017-Q2 | 3,566.00 | 45.70 | 41.00 | 14.00 | 4.70 | 13.30 | 3,598.00 | 3,598.00 | 0.00 | False |
| FY2017 Q3 | 2017-Q3 | 3,598.00 | 81.30 | 42.00 | 14.00 | 39.30 | -41.30 | 3,610.00 | 3,610.00 | 0.00 | False |
| FY2017 Q4 | 2017-Q4 | 3,610.00 | -268.90 | 40.80 | 17.60 | -309.70 | -18.30 | 3,299.60 | 3,299.60 | 0.00 | False |
| FY2018 Q1 | 2018-Q1 | 3,299.60 | 59.50 | 40.50 | 21.60 | 19.00 | 19.90 | 3,360.10 | 3,360.10 | 0.00 | False |
| FY2018 Q2 | 2018-Q2 | 3,360.10 | 143.80 | 40.60 | 17.80 | 103.20 | 28.50 | 3,509.60 | 3,509.60 | 0.00 | False |
| FY2018 Q3 | 2018-Q3 | 3,509.60 | 111.78 | 40.60 | 16.90 | 71.18 | -190.18 | 3,407.50 | 3,407.50 | 0.00 | False |
| FY2018 Q4 | 2018-Q4 | 3,407.50 | 24.90 | 40.70 | 18.20 | -15.80 | 47.50 | 3,457.40 | 3,457.40 | -0.00 | False |
| FY2019 Q1 | 2019-Q1 | 3,457.40 | 109.00 | 40.60 | 21.50 | 68.40 | -126.30 | 3,421.00 | 3,421.00 | 0.00 | False |
| FY2019 Q2 | 2019-Q2 | 3,421.00 | 170.30 | 50.70 | 21.30 | 119.60 | -99.90 | 3,462.00 | 3,462.00 | 0.00 | False |
| FY2019 Q3 | 2019-Q3 | 3,462.00 | 120.00 | 50.30 | 22.50 | 69.70 | -200.30 | 3,353.90 | 3,353.90 | 0.00 | False |
Equity residual outlier quarters: 0 Largest abs capital-actions / OCI residual: 365.70 USDm
8) Roll foward section - Debt¶
Same logic a
debt_view = debt_q[
[
'Debt_Open', 'Borrowings', 'Repayments', 'Debt', 'Debt_Reported',
'Debt_Error', 'Debt_Change', 'AverageDebt', 'InterestRate_Implied',
'InterestExpense_Reported', 'InterestExpense_Modeled', 'DebtMovement_OutlierFlag'
]
]
display(debt_view)
print(f'Debt movement outlier quarters: {int(debt["DebtMovement_OutlierFlag"].sum())}')
print(f"Largest abs debt change: {debt['Debt_Change'].abs().max():,.2f} USDm")
| Debt_Open | Borrowings | Repayments | Debt | Debt_Reported | Debt_Error | Debt_Change | AverageDebt | InterestRate_Implied | InterestExpense_Reported | InterestExpense_Modeled | DebtMovement_OutlierFlag | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Quarter | ||||||||||||
| FY2016 Q4 | 999.50 | 0.00 | -0.00 | 999.50 | 999.50 | 0.00 | 0.00 | 999.50 | 0.01 | 7.00 | 7.00 | False |
| FY2017 Q1 | 999.50 | 0.00 | 21.50 | 978.00 | 978.00 | 0.00 | -21.50 | 988.75 | 0.00 | 3.40 | 3.40 | False |
| FY2017 Q2 | 978.00 | 0.00 | 5.00 | 973.00 | 973.00 | 0.00 | -5.00 | 975.50 | 0.00 | 4.10 | 4.10 | False |
| FY2017 Q3 | 973.00 | 0.00 | 110.00 | 863.00 | 863.00 | 0.00 | -110.00 | 918.00 | 0.00 | 3.60 | 3.60 | False |
| FY2017 Q4 | 863.00 | 0.00 | 1.30 | 861.70 | 861.70 | 0.00 | -1.30 | 862.35 | 0.00 | 1.40 | 1.40 | False |
| FY2018 Q1 | 861.70 | 0.00 | 2.50 | 859.20 | 859.20 | 0.00 | -2.50 | 860.45 | 0.01 | 5.00 | 5.00 | False |
| FY2018 Q2 | 859.20 | 0.00 | 4.10 | 855.10 | 855.10 | 0.00 | -4.10 | 857.15 | 0.01 | 4.60 | 4.60 | False |
| FY2018 Q3 | 855.10 | 0.00 | 6.10 | 849.00 | 849.00 | 0.00 | -6.10 | 852.05 | 0.01 | 4.80 | 4.80 | False |
| FY2018 Q4 | 849.00 | 3.10 | 0.00 | 852.10 | 852.10 | 0.00 | 3.10 | 850.55 | 0.00 | 3.80 | 3.80 | False |
| FY2019 Q1 | 852.10 | 0.00 | 16.00 | 836.10 | 836.10 | 0.00 | -16.00 | 844.10 | 0.01 | 4.40 | 4.40 | False |
| FY2019 Q2 | 836.10 | 91.90 | 0.00 | 928.00 | 928.00 | 0.00 | 91.90 | 882.05 | 0.01 | 6.00 | 6.00 | False |
| FY2019 Q3 | 928.00 | 0.00 | 1.40 | 926.60 | 926.60 | 0.00 | -1.40 | 927.30 | 0.01 | 5.20 | 5.20 | False |
| FY2019 Q4 | 926.60 | 0.00 | 2.60 | 924.00 | 924.00 | 0.00 | -2.60 | 925.30 | 0.01 | 5.10 | 5.10 | False |
| FY2020 Q1 | 924.00 | 1,756.20 | 0.00 | 2,680.20 | 2,680.20 | 0.00 | 1,756.20 | 1,802.10 | 0.00 | 4.20 | 4.20 | True |
| FY2020 Q2 | 2,680.20 | 144.50 | 0.00 | 2,824.70 | 2,824.70 | 0.00 | 144.50 | 2,752.45 | 0.00 | 4.40 | 4.40 | False |
| FY2020 Q3 | 2,824.70 | 0.00 | 3.90 | 2,820.80 | 2,820.80 | 0.00 | -3.90 | 2,822.75 | 0.00 | 4.20 | 4.20 | False |
| FY2020 Q4 | 2,820.80 | 406.10 | 0.00 | 3,226.90 | 3,226.90 | 0.00 | 406.10 | 3,023.85 | 0.00 | 4.80 | 4.80 | True |
| FY2021 Q1 | 3,226.90 | 734.60 | 0.00 | 3,961.50 | 3,961.50 | 0.00 | 734.60 | 3,594.20 | 0.00 | 9.60 | 9.60 | True |
| FY2021 Q2 | 3,961.50 | 0.00 | 353.60 | 3,607.90 | 3,607.90 | 0.00 | -353.60 | 3,784.70 | 0.00 | 12.80 | 12.80 | True |
| FY2021 Q3 | 3,607.90 | 99.50 | 0.00 | 3,707.40 | 3,707.40 | 0.00 | 99.50 | 3,657.65 | 0.00 | 12.20 | 12.20 | False |
| FY2021 Q4 | 3,707.40 | 0.00 | 86.90 | 3,620.50 | 3,620.50 | 0.00 | -86.90 | 3,663.95 | 0.00 | 13.90 | 13.90 | False |
| FY2022 Q1 | 3,620.50 | 0.00 | 87.90 | 3,532.60 | 3,532.60 | 0.00 | -87.90 | 3,576.55 | 0.00 | 13.30 | 13.30 | False |
| FY2022 Q2 | 3,532.60 | 0.00 | 45.70 | 3,486.90 | 3,486.90 | 0.00 | -45.70 | 3,509.75 | 0.00 | 13.60 | 13.60 | False |
| FY2022 Q3 | 3,486.90 | 0.00 | 48.20 | 3,438.70 | 3,438.70 | 0.00 | -48.20 | 3,462.80 | 0.00 | 13.40 | 13.40 | False |
| FY2022 Q4 | 3,438.70 | 0.00 | 46.80 | 3,391.90 | 3,391.90 | 0.00 | -46.80 | 3,415.30 | 0.00 | 13.70 | 13.70 | False |
| FY2023 Q1 | 3,391.90 | 0.00 | 581.30 | 2,810.60 | 2,810.60 | 0.00 | -581.30 | 3,101.25 | 0.00 | 11.80 | 11.80 | True |
| FY2023 Q2 | 2,810.60 | 0.00 | 48.80 | 2,761.80 | 2,761.80 | 0.00 | -48.80 | 2,786.20 | 0.00 | 9.50 | 9.50 | False |
| FY2023 Q3 | 2,761.80 | 60.60 | 0.00 | 2,822.40 | 2,822.40 | 0.00 | 60.60 | 2,792.10 | 0.00 | 12.00 | 12.00 | False |
| FY2023 Q4 | 2,822.40 | 59.50 | 0.00 | 2,881.90 | 2,881.90 | 0.00 | 59.50 | 2,852.15 | 0.00 | 7.10 | 7.10 | False |
| FY2024 Q1 | 2,881.90 | 0.00 | 42.40 | 2,839.50 | 2,839.50 | 0.00 | -42.40 | 2,860.70 | 0.00 | 10.00 | 10.00 | False |
| FY2024 Q2 | 2,839.50 | 0.00 | 64.60 | 2,774.90 | 2,774.90 | 0.00 | -64.60 | 2,807.20 | 0.00 | 10.00 | 10.00 | False |
| FY2024 Q3 | 2,774.90 | 0.00 | 18.80 | 2,756.10 | 2,756.10 | 0.00 | -18.80 | 2,765.50 | 0.00 | 10.60 | 10.60 | False |
| FY2024 Q4 | 2,756.10 | 0.00 | 80.80 | 2,675.30 | 2,675.30 | 0.00 | -80.80 | 2,715.70 | 0.00 | 11.60 | 11.60 | False |
| FY2025 Q1 | 2,675.30 | 6.90 | 0.00 | 2,682.20 | 2,682.20 | 0.00 | 6.90 | 2,678.75 | 0.00 | 10.90 | 10.90 | False |
| FY2025 Q2 | 2,682.20 | 0.00 | 14.30 | 2,667.90 | 2,667.90 | 0.00 | -14.30 | 2,675.05 | 0.00 | 11.40 | 11.40 | False |
| FY2025 Q3 | 2,667.90 | 15.80 | 0.00 | 2,683.70 | 2,683.70 | 0.00 | 15.80 | 2,675.80 | 0.00 | 11.60 | 11.60 | False |
| FY2025 Q4 | 2,683.70 | 0.00 | 15.60 | 2,668.10 | 2,668.10 | 0.00 | -15.60 | 2,675.90 | 0.00 | 10.20 | 10.20 | False |
| FY2026 Q1 | 2,668.10 | 1,680.70 | 0.00 | 4,348.80 | 4,348.80 | 0.00 | 1,680.70 | 3,508.45 | 0.00 | 11.50 | 11.50 | True |
| FY2026 Q2 | 4,348.80 | 0.00 | 325.50 | 4,023.30 | 4,023.30 | 0.00 | -325.50 | 4,186.05 | 0.00 | 15.90 | 15.90 | True |
| FY2026 Q3 | 4,023.30 | 0.00 | 9.30 | 4,014.00 | 4,014.00 | 0.00 | -9.30 | 4,018.65 | 0.00 | 13.30 | 13.30 | False |
Debt movement outlier quarters: 7 Largest abs debt change: 1,756.20 USDm
9) Assemble the BS¶
balance_view = balance_sheet_q[
[
'ReportedCash', 'Cash_Reconciled', 'CashPlugGap', 'ShortTermInvestments',
'AR', 'Inventory', 'OtherCurrentAssets', 'CurrentAssetResidual', 'OtherAssetResidual',
'PPE_Net', 'TotalAssets', 'AP', 'AccruedExpenses', 'DeferredRevenue',
'OtherCurrentLiabilities', 'Debt', 'OtherLiabilityResidual', 'TotalLiabilities',
'TotalEquity', 'BalanceError'
]
]
display(balance_view.tail(8))
print(f"Max abs balance error: {balance_sheet['BalanceError'].abs().max():,.6f}")
print(f"Max abs cash plug gap: {balance_sheet['CashPlugGap'].abs().max():,.6f}")
| ReportedCash | Cash_Reconciled | CashPlugGap | ShortTermInvestments | AR | Inventory | OtherCurrentAssets | CurrentAssetResidual | OtherAssetResidual | PPE_Net | TotalAssets | AP | AccruedExpenses | DeferredRevenue | OtherCurrentLiabilities | Debt | OtherLiabilityResidual | TotalLiabilities | TotalEquity | BalanceError | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Quarter | ||||||||||||||||||||
| FY2024 Q4 | 1,662.20 | 1,662.20 | -0.00 | 121.00 | 446.50 | 902.20 | 55.70 | 172.20 | 1,377.80 | 1,865.00 | 6,602.60 | 332.20 | 632.30 | 17.30 | 32.60 | 2,675.30 | 462.60 | 4,152.30 | 2,450.30 | 0.00 |
| FY2025 Q1 | 1,586.90 | 1,586.90 | 0.00 | 173.60 | 371.80 | 1,039.10 | 64.90 | 211.60 | 1,347.80 | 1,845.30 | 6,641.00 | 477.80 | 604.40 | 18.30 | 28.30 | 2,682.20 | 463.00 | 4,274.00 | 2,367.00 | 0.00 |
| FY2025 Q2 | 1,355.00 | 1,355.00 | 0.00 | 334.70 | 517.90 | 1,127.90 | 67.60 | 201.50 | 1,349.40 | 1,846.00 | 6,800.00 | 495.70 | 687.80 | 15.70 | 30.00 | 2,667.90 | 460.60 | 4,357.70 | 2,442.30 | 0.00 |
| FY2025 Q3 | 1,940.20 | 1,940.20 | -0.00 | 203.00 | 435.20 | 998.60 | 112.20 | 211.10 | 1,331.30 | 1,849.30 | 7,080.90 | 489.50 | 777.80 | 22.50 | 16.10 | 2,683.70 | 552.10 | 4,541.70 | 2,539.20 | -0.00 |
| FY2025 Q4 | 1,922.50 | 1,922.50 | 0.00 | 160.50 | 459.50 | 949.60 | 82.10 | 215.70 | 1,397.90 | 1,859.50 | 7,047.30 | 436.00 | 736.50 | 16.60 | 23.20 | 2,668.10 | 578.40 | 4,458.80 | 2,588.50 | 0.00 |
| FY2026 Q1 | 2,090.20 | 2,090.20 | -0.00 | 186.60 | 396.60 | 1,222.20 | 56.70 | 246.20 | 1,447.70 | 2,105.50 | 7,751.70 | 609.10 | 657.50 | 15.00 | 41.90 | 4,348.80 | -435.90 | 5,236.40 | 2,515.30 | 0.00 |
| FY2026 Q2 | 1,444.40 | 1,444.40 | 0.00 | 202.50 | 524.60 | 1,261.30 | 64.10 | 256.10 | 1,405.10 | 2,189.60 | 7,347.70 | 532.70 | 246.10 | 21.80 | 738.20 | 4,023.30 | -796.60 | 4,765.50 | 2,582.20 | 0.00 |
| FY2026 Q3 | 2,034.00 | 2,034.00 | 0.00 | 218.90 | 460.70 | 1,149.40 | 68.30 | 258.00 | 1,413.00 | 2,211.50 | 7,813.80 | 543.70 | 292.60 | 27.60 | 791.30 | 4,014.00 | -743.80 | 4,925.40 | 2,888.40 | 0.00 |
Max abs balance error: 0.000000 Max abs cash plug gap: 0.000000
11) Balance check report¶
Balance sheet output and checked.
display(balance_check_q)
flagged_periods = balance_check_q[balance_check_q['FlagCount'] > 0]
if flagged_periods.empty:
print('No balance-check flags triggered.')
else:
display(flagged_periods)
| PeriodKey | Cash_Reconciled | ReportedCash | CashPlugGap | BalanceError | AssetsGap | LiabilitiesGap | EquityGap | CCC | WC_CashImpact_Gap | PPE_OtherAdjustments | CapitalActionsAndOCI | Debt_Change | NegativeCashFlag | NegativeARFlag | NegativeInventoryFlag | NegativeAPFlag | NegativePPEFlag | NegativeDebtFlag | NegativeTotalEquityFlag | CCC_OutlierFlag | PPE_Adjustment_OutlierFlag | Equity_Adjustment_OutlierFlag | DebtMovement_OutlierFlag | CashPlugMismatchFlag | BalanceErrorFlag | NegativeBalanceCount | OutlierCount | FlagCount | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Quarter | |||||||||||||||||||||||||||||
| FY2016 Q4 | 2016-Q4 | 456.30 | 456.30 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 128.90 | NaN | 0.00 | 0.00 | 0.00 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2017 Q1 | 2017-Q1 | 457.00 | 457.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 165.57 | 76.80 | -90.00 | -121.40 | -21.50 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2017 Q2 | 2017-Q2 | 434.00 | 434.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 131.35 | 83.00 | -12.00 | 13.30 | -5.00 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2017 Q3 | 2017-Q3 | 928.00 | 928.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 118.23 | -27.00 | -104.00 | -41.30 | -110.00 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2017 Q4 | 2017-Q4 | 668.30 | 668.30 | -0.00 | -0.00 | 0.00 | 0.00 | 0.00 | 104.91 | 46.00 | -181.50 | -18.30 | -1.30 | False | False | False | False | False | False | False | False | True | False | False | False | False | 0 | 1 | 1 |
| FY2018 Q1 | 2018-Q1 | 830.40 | 830.40 | -0.00 | 0.00 | 0.00 | -0.00 | 0.00 | 147.53 | -78.10 | -11.70 | 19.90 | -2.50 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2018 Q2 | 2018-Q2 | 1,111.60 | 1,111.60 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 120.23 | -7.90 | 8.20 | 28.50 | -4.10 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2018 Q3 | 2018-Q3 | 1,175.70 | 1,175.70 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 107.02 | -220.00 | -0.20 | -190.18 | -6.10 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2018 Q4 | 2018-Q4 | 1,304.60 | 1,304.60 | -0.00 | -0.00 | -0.00 | 0.00 | -0.00 | 112.72 | 173.70 | 7.60 | 47.50 | 3.10 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2019 Q1 | 2019-Q1 | 532.30 | 532.30 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 143.79 | -47.20 | -16.60 | -126.30 | -16.00 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2019 Q2 | 2019-Q2 | 553.10 | 553.10 | 0.00 | -0.00 | 0.00 | 0.00 | 0.00 | 132.63 | -24.10 | 7.70 | -99.90 | 91.90 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2019 Q3 | 2019-Q3 | 680.50 | 680.50 | 0.00 | -0.00 | 0.00 | 0.00 | 0.00 | 118.16 | -54.40 | -35.30 | -200.30 | -1.40 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2019 Q4 | 2019-Q4 | 584.10 | 584.10 | -0.00 | -0.00 | -0.00 | 0.00 | 0.00 | 117.08 | 86.40 | -19.30 | -72.50 | -2.60 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2020 Q1 | 2020-Q1 | 648.40 | 648.40 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 133.01 | -53.80 | 1,380.40 | -365.70 | 1,756.20 | False | False | False | False | False | False | False | False | True | False | True | False | False | 0 | 2 | 2 |
| FY2020 Q2 | 2020-Q2 | 548.10 | 548.10 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 117.79 | 173.90 | 160.70 | -256.00 | 144.50 | False | False | False | False | False | False | False | False | True | False | False | False | False | 0 | 1 | 1 |
| FY2020 Q3 | 2020-Q3 | 1,079.90 | 1,079.90 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 106.07 | 3.10 | 6.30 | 1.70 | -3.90 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2020 Q4 | 2020-Q4 | 1,620.40 | 1,620.40 | -0.00 | 0.00 | 0.00 | 0.00 | -0.00 | 85.55 | 66.40 | -96.30 | -113.20 | 406.10 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
| FY2021 Q1 | 2021-Q1 | 2,451.30 | 2,451.30 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 433.86 | -76.80 | -38.80 | 69.70 | 734.60 | False | False | False | False | False | False | False | True | False | False | True | False | False | 0 | 2 | 2 |
| FY2021 Q2 | 2021-Q2 | 2,012.00 | 2,012.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 166.57 | 17.90 | -53.20 | -1.80 | -353.60 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
| FY2021 Q3 | 2021-Q3 | 2,621.50 | 2,621.50 | 0.00 | -0.00 | -0.00 | -0.00 | 0.00 | 120.24 | -35.10 | 131.60 | 8.30 | 99.50 | False | False | False | False | False | False | False | False | True | False | False | False | False | 0 | 1 | 1 |
| FY2021 Q4 | 2021-Q4 | 2,579.00 | 2,579.00 | 0.00 | -0.00 | 0.00 | 0.00 | 0.00 | 104.98 | 136.10 | -137.40 | -70.60 | -86.90 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2022 Q1 | 2022-Q1 | 2,596.40 | 2,596.40 | -0.00 | 0.00 | -0.00 | -0.00 | 0.00 | 121.07 | 7.30 | -68.60 | -69.80 | -87.90 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2022 Q2 | 2022-Q2 | 2,387.90 | 2,387.90 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 114.43 | 34.30 | -14.90 | -19.90 | -45.70 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2022 Q3 | 2022-Q3 | 2,276.80 | 2,276.80 | -0.00 | -0.00 | -0.00 | -0.00 | 0.00 | 88.09 | 52.40 | -17.30 | -329.00 | -48.20 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2022 Q4 | 2022-Q4 | 1,863.80 | 1,863.80 | -0.00 | 0.00 | -0.00 | -0.00 | 0.00 | 110.95 | 52.50 | -9.10 | -181.50 | -46.80 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2023 Q1 | 2023-Q1 | 1,456.80 | 1,456.80 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 136.37 | -1.00 | -79.50 | -265.40 | -581.30 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
| FY2023 Q2 | 2023-Q2 | 1,107.10 | 1,107.10 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 153.39 | 3.20 | -61.30 | -230.50 | -48.80 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2023 Q3 | 2023-Q3 | 1,566.10 | 1,566.10 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 130.68 | -55.20 | 87.90 | 26.20 | 60.60 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2023 Q4 | 2023-Q4 | 1,529.30 | 1,529.30 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 134.69 | 91.60 | 64.60 | -35.70 | 59.50 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2024 Q1 | 2024-Q1 | 1,607.20 | 1,607.20 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 166.34 | 82.10 | -34.20 | -93.80 | -42.40 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2024 Q2 | 2024-Q2 | 1,381.80 | 1,381.80 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 144.95 | -39.60 | -57.10 | -199.00 | -64.60 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2024 Q3 | 2024-Q3 | 1,803.60 | 1,803.60 | 0.00 | -0.00 | 0.00 | 0.00 | 0.00 | 109.64 | -56.30 | 0.90 | -50.00 | -18.80 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2024 Q4 | 2024-Q4 | 1,662.20 | 1,662.20 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 125.21 | 30.20 | -69.90 | -188.60 | -80.80 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2025 Q1 | 2025-Q1 | 1,586.90 | 1,586.90 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 137.17 | -12.70 | 1.30 | -228.70 | 6.90 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2025 Q2 | 2025-Q2 | 1,355.00 | 1,355.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 128.53 | 19.30 | 14.90 | -56.90 | -14.30 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2025 Q3 | 2025-Q3 | 1,940.20 | 1,940.20 | -0.00 | -0.00 | -0.00 | -0.00 | -0.00 | 87.11 | -100.60 | -3.60 | -174.00 | 15.80 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2025 Q4 | 2025-Q4 | 1,922.50 | 1,922.50 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 112.80 | -40.30 | -14.70 | -51.90 | -15.60 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| FY2026 Q1 | 2026-Q1 | 2,090.20 | 2,090.20 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 138.39 | 13.70 | 114.20 | -265.30 | 1,680.70 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
| FY2026 Q2 | 2026-Q2 | 1,444.40 | 1,444.40 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 127.00 | 293.40 | 47.30 | -110.10 | -325.50 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
| FY2026 Q3 | 2026-Q3 | 2,034.00 | 2,034.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 93.78 | -93.70 | 6.00 | -25.00 | -9.30 | False | False | False | False | False | False | False | False | False | False | False | False | False | 0 | 0 | 0 |
| PeriodKey | Cash_Reconciled | ReportedCash | CashPlugGap | BalanceError | AssetsGap | LiabilitiesGap | EquityGap | CCC | WC_CashImpact_Gap | PPE_OtherAdjustments | CapitalActionsAndOCI | Debt_Change | NegativeCashFlag | NegativeARFlag | NegativeInventoryFlag | NegativeAPFlag | NegativePPEFlag | NegativeDebtFlag | NegativeTotalEquityFlag | CCC_OutlierFlag | PPE_Adjustment_OutlierFlag | Equity_Adjustment_OutlierFlag | DebtMovement_OutlierFlag | CashPlugMismatchFlag | BalanceErrorFlag | NegativeBalanceCount | OutlierCount | FlagCount | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Quarter | |||||||||||||||||||||||||||||
| FY2017 Q4 | 2017-Q4 | 668.30 | 668.30 | -0.00 | -0.00 | 0.00 | 0.00 | 0.00 | 104.91 | 46.00 | -181.50 | -18.30 | -1.30 | False | False | False | False | False | False | False | False | True | False | False | False | False | 0 | 1 | 1 |
| FY2020 Q1 | 2020-Q1 | 648.40 | 648.40 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 133.01 | -53.80 | 1,380.40 | -365.70 | 1,756.20 | False | False | False | False | False | False | False | False | True | False | True | False | False | 0 | 2 | 2 |
| FY2020 Q2 | 2020-Q2 | 548.10 | 548.10 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 117.79 | 173.90 | 160.70 | -256.00 | 144.50 | False | False | False | False | False | False | False | False | True | False | False | False | False | 0 | 1 | 1 |
| FY2020 Q4 | 2020-Q4 | 1,620.40 | 1,620.40 | -0.00 | 0.00 | 0.00 | 0.00 | -0.00 | 85.55 | 66.40 | -96.30 | -113.20 | 406.10 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
| FY2021 Q1 | 2021-Q1 | 2,451.30 | 2,451.30 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 433.86 | -76.80 | -38.80 | 69.70 | 734.60 | False | False | False | False | False | False | False | True | False | False | True | False | False | 0 | 2 | 2 |
| FY2021 Q2 | 2021-Q2 | 2,012.00 | 2,012.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 166.57 | 17.90 | -53.20 | -1.80 | -353.60 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
| FY2021 Q3 | 2021-Q3 | 2,621.50 | 2,621.50 | 0.00 | -0.00 | -0.00 | -0.00 | 0.00 | 120.24 | -35.10 | 131.60 | 8.30 | 99.50 | False | False | False | False | False | False | False | False | True | False | False | False | False | 0 | 1 | 1 |
| FY2023 Q1 | 2023-Q1 | 1,456.80 | 1,456.80 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 136.37 | -1.00 | -79.50 | -265.40 | -581.30 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
| FY2026 Q1 | 2026-Q1 | 2,090.20 | 2,090.20 | -0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 138.39 | 13.70 | 114.20 | -265.30 | 1,680.70 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
| FY2026 Q2 | 2026-Q2 | 1,444.40 | 1,444.40 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 127.00 | 293.40 | 47.30 | -110.10 | -325.50 | False | False | False | False | False | False | False | False | False | False | True | False | False | 0 | 1 | 1 |
#asseting that max error can be 0.01
assert balance_check['BalanceError'].abs().max() < 1e-2
assert balance_check['CashPlugGap'].abs().max() < 1e-2
print('Verification passed: the reconstructed balance sheet balances and the reconciled cash line matches the reported cash balance.')
Verification passed: the reconstructed balance sheet balances and the reconciled cash line matches the reported cash balance.
for name, output_path in output_files.items():
print(f'{name}: {output_path}')
working_capital_csv: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini-projects/outputs/chapter4_working_capital_schedule_usdm.csv ppe_csv: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini-projects/outputs/chapter4_ppe_schedule_usdm.csv equity_csv: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini-projects/outputs/chapter4_equity_schedule_usdm.csv debt_csv: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini-projects/outputs/chapter4_debt_schedule_usdm.csv balance_sheet_csv: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini-projects/outputs/chapter4_balance_sheet_usdm.csv balance_check_csv: /Users/theomachado/Library/CloudStorage/OneDrive-UniversityofFlorida/Spring 2026/FIN4453 - OFC/Mini-projects/outputs/chapter4_balance_check_report.csv