[{"data":1,"prerenderedAt":1977},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-covenant-compliance-report":3,"surround:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-covenant-compliance-report":1943,"ancestors:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-covenant-compliance-report":1951,"children:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-covenant-compliance-report":1956,"related:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-covenant-compliance-report":1957},{"id":4,"title":5,"body":6,"dateModified":1909,"datePublished":1909,"description":1910,"extension":1911,"faq":1912,"keyword":1921,"kind":1922,"meta":1923,"navigation":101,"path":1932,"seo":1933,"slug":1936,"stem":1937,"topics":1938,"type":1941,"__hash__":1942},"docs\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-covenant-compliance-report\u002Findex.md","Build a Loan Covenant Compliance Report",{"type":7,"value":8,"toc":1898},"minimark",[9,32,37,66,69,73,344,347,502,506,920,935,938,1004,1008,1124,1127,1131,1557,1564,1784,1788,1823,1827,1830,1834,1843,1849,1855,1861,1865,1894],[10,11,12,13,17,18,21,22,25,26,31],"p",{},"A business with a bank loan usually signs up to ",[14,15,16],"em",{},"covenants"," — financial tests it must pass every quarter, typically that debt stays below a multiple of earnings, that earnings comfortably cover interest, and that a minimum amount of cash is held. Breaching one can let the bank demand repayment, so finance teams track them closely and send a compliance certificate with the quarter's figures. The calculations are simple but fiddly: the tests use the ",[14,19,20],{},"last twelve months"," of earnings, not the quarter's; net debt is measured at a point in time; definitions follow the loan agreement, not the accounts. And the number management really wants is not the ratio but the ",[14,23,24],{},"headroom"," — how far earnings could fall before a test fails. This page builds the quarterly covenant report with pandas from monthly management accounts: rolling twelve-month EBITDA and interest, net debt, the three tests against their limits, headroom in money terms, a look ahead using the forecast, and a workbook whose ratios are live formulas a lender can check. It belongs to ",[27,28,30],"a",{"href":29},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002F","Building Finance and Operations Reports",".",[33,34,36],"h2",{"id":35},"prerequisites","Prerequisites",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[45,46,47],"code",{"__ignoreMap":43},[48,49,52,56,60,63],"span",{"class":50,"line":51},"line",1,[48,53,55],{"class":54},"sMTad","pip",[48,57,59],{"class":58},"srMev"," install",[48,61,62],{"class":58}," pandas",[48,64,65],{"class":58}," openpyxl\n",[10,67,68],{},"The input is monthly management accounts from January 2025, with EBITDA, interest paid, gross debt and cash at month end, actuals to September 2026 and the forecast for the following two quarters. The loan agreement sets three covenants, tested at each quarter end: leverage (net debt ÷ last-twelve-month EBITDA) at most 3.0×, interest cover (LTM EBITDA ÷ LTM interest) at least 4.0×, and cash of at least £750,000.",[33,70,72],{"id":71},"step-1-twelve-month-figures-at-each-quarter-end","Step 1 — Twelve-month figures at each quarter end",[38,74,78],{"className":75,"code":76,"language":77,"meta":43,"style":43},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nacc = pd.read_excel(\"monthly_accounts.xlsx\")\nacc[\"Month\"] = pd.PeriodIndex(acc[\"Month\"], freq=\"M\")\nacc = acc.set_index(\"Month\")\nacc[\"LTM EBITDA\"] = acc[\"EBITDA\"].rolling(12).sum()\nacc[\"LTM interest\"] = acc[\"Interest\"].rolling(12).sum()\nacc[\"Net debt\"] = acc[\"Gross debt\"] - acc[\"Cash\"]\n\nquarter_ends = acc[acc.index.month.isin([3, 6, 9, 12]) & acc[\"LTM EBITDA\"].notna()]\ntests = quarter_ends[[\"Type\", \"LTM EBITDA\", \"LTM interest\", \"Net debt\", \"Cash\"]].copy()\nprint(tests.round(0).to_string())\n","python",[45,79,80,96,103,121,154,168,196,219,249,254,296,329],{"__ignoreMap":43},[48,81,82,86,90,93],{"class":50,"line":51},[48,83,85],{"class":84},"s-kum","import",[48,87,89],{"class":88},"skGVy"," pandas ",[48,91,92],{"class":84},"as",[48,94,95],{"class":88}," pd\n",[48,97,99],{"class":50,"line":98},2,[48,100,102],{"emptyLinePlaceholder":101},true,"\n",[48,104,106,109,112,115,118],{"class":50,"line":105},3,[48,107,108],{"class":88},"acc ",[48,110,111],{"class":84},"=",[48,113,114],{"class":88}," pd.read_excel(",[48,116,117],{"class":58},"\"monthly_accounts.xlsx\"",[48,119,120],{"class":88},")\n",[48,122,124,127,130,133,135,138,140,143,147,149,152],{"class":50,"line":123},4,[48,125,126],{"class":88},"acc[",[48,128,129],{"class":58},"\"Month\"",[48,131,132],{"class":88},"] ",[48,134,111],{"class":84},[48,136,137],{"class":88}," pd.PeriodIndex(acc[",[48,139,129],{"class":58},[48,141,142],{"class":88},"], ",[48,144,146],{"class":145},"sa561","freq",[48,148,111],{"class":84},[48,150,151],{"class":58},"\"M\"",[48,153,120],{"class":88},[48,155,157,159,161,164,166],{"class":50,"line":156},5,[48,158,108],{"class":88},[48,160,111],{"class":84},[48,162,163],{"class":88}," acc.set_index(",[48,165,129],{"class":58},[48,167,120],{"class":88},[48,169,171,173,176,178,180,183,186,189,193],{"class":50,"line":170},6,[48,172,126],{"class":88},[48,174,175],{"class":58},"\"LTM EBITDA\"",[48,177,132],{"class":88},[48,179,111],{"class":84},[48,181,182],{"class":88}," acc[",[48,184,185],{"class":58},"\"EBITDA\"",[48,187,188],{"class":88},"].rolling(",[48,190,192],{"class":191},"sP0c6","12",[48,194,195],{"class":88},").sum()\n",[48,197,199,201,204,206,208,210,213,215,217],{"class":50,"line":198},7,[48,200,126],{"class":88},[48,202,203],{"class":58},"\"LTM interest\"",[48,205,132],{"class":88},[48,207,111],{"class":84},[48,209,182],{"class":88},[48,211,212],{"class":58},"\"Interest\"",[48,214,188],{"class":88},[48,216,192],{"class":191},[48,218,195],{"class":88},[48,220,222,224,227,229,231,233,236,238,241,243,246],{"class":50,"line":221},8,[48,223,126],{"class":88},[48,225,226],{"class":58},"\"Net debt\"",[48,228,132],{"class":88},[48,230,111],{"class":84},[48,232,182],{"class":88},[48,234,235],{"class":58},"\"Gross debt\"",[48,237,132],{"class":88},[48,239,240],{"class":84},"-",[48,242,182],{"class":88},[48,244,245],{"class":58},"\"Cash\"",[48,247,248],{"class":88},"]\n",[48,250,252],{"class":50,"line":251},9,[48,253,102],{"emptyLinePlaceholder":101},[48,255,257,260,262,265,268,271,274,276,279,281,283,286,289,291,293],{"class":50,"line":256},10,[48,258,259],{"class":88},"quarter_ends ",[48,261,111],{"class":84},[48,263,264],{"class":88}," acc[acc.index.month.isin([",[48,266,267],{"class":191},"3",[48,269,270],{"class":88},", ",[48,272,273],{"class":191},"6",[48,275,270],{"class":88},[48,277,278],{"class":191},"9",[48,280,270],{"class":88},[48,282,192],{"class":191},[48,284,285],{"class":88},"]) ",[48,287,288],{"class":84},"&",[48,290,182],{"class":88},[48,292,175],{"class":58},[48,294,295],{"class":88},"].notna()]\n",[48,297,299,302,304,307,310,312,314,316,318,320,322,324,326],{"class":50,"line":298},11,[48,300,301],{"class":88},"tests ",[48,303,111],{"class":84},[48,305,306],{"class":88}," quarter_ends[[",[48,308,309],{"class":58},"\"Type\"",[48,311,270],{"class":88},[48,313,175],{"class":58},[48,315,270],{"class":88},[48,317,203],{"class":58},[48,319,270],{"class":88},[48,321,226],{"class":58},[48,323,270],{"class":88},[48,325,245],{"class":58},[48,327,328],{"class":88},"]].copy()\n",[48,330,332,335,338,341],{"class":50,"line":331},12,[48,333,334],{"class":191},"print",[48,336,337],{"class":88},"(tests.round(",[48,339,340],{"class":191},"0",[48,342,343],{"class":88},").to_string())\n",[10,345,346],{},"Covenant tests almost always use a rolling twelve-month figure for earnings and interest, so one weak or strong month does not swing the result. A twelve-month rolling sum over monthly figures gives that for every month; the quarter-end rows are the ones the tests use. The first test date with a full twelve months of history is December 2025, when twelve-month EBITDA was £4.94 million against net debt of £13.27 million. Net debt is a balance-sheet figure at the test date — gross borrowing less cash — and the cash test uses the month-end balance directly. Loan agreements often adjust these definitions — adding back one-off costs to EBITDA, excluding certain leases from debt — and those adjustments belong in this step, written out explicitly, so the calculation matches the agreement rather than the management accounts.",[348,349,357,358,357,362,357,366,357,373,357,382,357,389,357,393,357,398,357,402,357,406,357,412,357,417,357,421,357,426,357,429,357,432,357,436,357,439,357,442,357,446,357,449,357,452,357,456,357,459,357,464,357,470,357,474,357,478,357,482,357,486,357,489,357,494,357,499],"svg",{"viewBox":350,"role":351,"ariaLabelledBy":352,"xmlns":355,"style":356},"4 0 752 144","img",[353,354],"cov-calc-t","cov-calc-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[359,360,361],"title",{"id":353},"From monthly accounts to covenant tests",[363,364,365],"desc",{"id":354},"Monthly EBITDA and interest are summed over the last twelve months. Net debt is gross debt less cash at the quarter end. Leverage, interest cover and cash are compared with their limits, and the headroom on each is expressed in pounds.",[367,368],"rect",{"x":369,"y":340,"width":370,"height":371,"fill":372},"4","752","144","#ffffff",[367,374],{"x":375,"y":376,"width":377,"height":378,"rx":192,"fill":379,"stroke":380,"style":381},"20.0","16.0","115.2","111.5","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[383,384,388],"text",{"x":385,"y":386,"style":387},"77.6","41.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Monthly",[383,390,392],{"x":385,"y":391,"style":387},"59.0","accounts",[383,394,397],{"x":385,"y":395,"style":396},"78.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","EBITDA,",[383,399,401],{"x":385,"y":400,"style":396},"96.0","interest, debt,",[383,403,405],{"x":385,"y":404,"style":396},"113.5","cash",[50,407],{"x1":408,"y1":409,"x2":410,"y2":409,"stroke":411,"style":381},"140.2","71.8","162.2","var(--brand,#5b5cf0)",[413,414],"polygon",{"points":415,"fill":416},"166.2,71.8 157.2,76.8 157.2,66.8","#5b5cf0",[367,418],{"x":419,"y":376,"width":377,"height":378,"rx":192,"fill":420,"stroke":411,"style":381},"171.2","#f0f4ff",[383,422,425],{"x":423,"y":391,"style":424},"228.8","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","LTM sums",[383,427,428],{"x":423,"y":395,"style":396},"rolling 12",[383,430,431],{"x":423,"y":400,"style":396},"months",[50,433],{"x1":434,"y1":409,"x2":435,"y2":409,"stroke":411,"style":381},"291.4","313.4",[413,437],{"points":438,"fill":416},"317.4,71.8 308.4,76.8 308.4,66.8",[367,440],{"x":441,"y":376,"width":377,"height":378,"rx":192,"fill":420,"stroke":411,"style":381},"322.4",[383,443,445],{"x":444,"y":391,"style":424},"380.0","Net debt",[383,447,448],{"x":444,"y":395,"style":396},"debt − cash at",[383,450,451],{"x":444,"y":400,"style":396},"quarter end",[50,453],{"x1":454,"y1":409,"x2":455,"y2":409,"stroke":411,"style":381},"442.6","464.6",[413,457],{"points":458,"fill":416},"468.6,71.8 459.6,76.8 459.6,66.8",[367,460],{"x":461,"y":376,"width":377,"height":378,"rx":192,"fill":462,"stroke":463,"style":381},"473.6","#fdefd8","var(--gold,#b4740a)",[383,465,469],{"x":466,"y":467,"style":468},"531.2","50.2","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Ratios vs",[383,471,473],{"x":466,"y":472,"style":468},"67.8","limits",[383,475,477],{"x":466,"y":476,"style":396},"87.2","≤ 3.0×, ≥ 4.0×,",[383,479,481],{"x":466,"y":480,"style":396},"104.8","≥ £750k",[50,483],{"x1":484,"y1":409,"x2":485,"y2":409,"stroke":411,"style":381},"593.8","615.8",[413,487],{"points":488,"fill":416},"619.8,71.8 610.8,76.8 610.8,66.8",[367,490],{"x":491,"y":376,"width":377,"height":378,"rx":192,"fill":492,"stroke":493,"style":381},"624.8","#d9f4f1","var(--teal,#0f9488)",[383,495,498],{"x":496,"y":472,"style":497},"682.4","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Headroom",[383,500,501],{"x":496,"y":476,"style":396},"in pounds",[33,503,505],{"id":504},"step-2-the-tests-and-their-headroom","Step 2 — The tests and their headroom",[38,507,509],{"className":75,"code":508,"language":77,"meta":43,"style":43},"LIMITS = {\"Leverage\": 3.0, \"Interest cover\": 4.0, \"Minimum cash\": 750_000}\n\ntests[\"Leverage\"] = tests[\"Net debt\"] \u002F tests[\"LTM EBITDA\"]\ntests[\"Interest cover\"] = tests[\"LTM EBITDA\"] \u002F tests[\"LTM interest\"]\ntests[\"Leverage OK\"] = tests[\"Leverage\"] \u003C= LIMITS[\"Leverage\"]\ntests[\"Cover OK\"] = tests[\"Interest cover\"] >= LIMITS[\"Interest cover\"]\ntests[\"Cash OK\"] = tests[\"Cash\"] >= LIMITS[\"Minimum cash\"]\n\n# headroom: how far LTM EBITDA (or cash) could fall before each test fails\ntests[\"EBITDA headroom (leverage)\"] = tests[\"LTM EBITDA\"] - tests[\"Net debt\"] \u002F LIMITS[\"Leverage\"]\ntests[\"EBITDA headroom (cover)\"] = tests[\"LTM EBITDA\"] - tests[\"LTM interest\"] * LIMITS[\"Interest cover\"]\ntests[\"Cash headroom\"] = tests[\"Cash\"] - LIMITS[\"Minimum cash\"]\ntests[\"Tightest\"] = tests[[\"EBITDA headroom (leverage)\", \"EBITDA headroom (cover)\"]].idxmin(axis=1).str.extract(r\"\\((\\w+)\\)\")[0]\nshow = [\"Type\", \"Leverage\", \"Interest cover\", \"Cash\", \"EBITDA headroom (leverage)\", \"EBITDA headroom (cover)\", \"Tightest\"]\nprint(tests[show].round(2).to_string())\n",[45,510,511,554,558,585,609,639,667,694,698,704,739,775,802,868,907],{"__ignoreMap":43},[48,512,513,516,519,522,525,528,531,533,536,538,541,543,546,548,551],{"class":50,"line":51},[48,514,515],{"class":191},"LIMITS",[48,517,518],{"class":84}," =",[48,520,521],{"class":88}," {",[48,523,524],{"class":58},"\"Leverage\"",[48,526,527],{"class":88},": ",[48,529,530],{"class":191},"3.0",[48,532,270],{"class":88},[48,534,535],{"class":58},"\"Interest cover\"",[48,537,527],{"class":88},[48,539,540],{"class":191},"4.0",[48,542,270],{"class":88},[48,544,545],{"class":58},"\"Minimum cash\"",[48,547,527],{"class":88},[48,549,550],{"class":191},"750_000",[48,552,553],{"class":88},"}\n",[48,555,556],{"class":50,"line":98},[48,557,102],{"emptyLinePlaceholder":101},[48,559,560,563,565,567,569,572,574,576,579,581,583],{"class":50,"line":105},[48,561,562],{"class":88},"tests[",[48,564,524],{"class":58},[48,566,132],{"class":88},[48,568,111],{"class":84},[48,570,571],{"class":88}," tests[",[48,573,226],{"class":58},[48,575,132],{"class":88},[48,577,578],{"class":84},"\u002F",[48,580,571],{"class":88},[48,582,175],{"class":58},[48,584,248],{"class":88},[48,586,587,589,591,593,595,597,599,601,603,605,607],{"class":50,"line":123},[48,588,562],{"class":88},[48,590,535],{"class":58},[48,592,132],{"class":88},[48,594,111],{"class":84},[48,596,571],{"class":88},[48,598,175],{"class":58},[48,600,132],{"class":88},[48,602,578],{"class":84},[48,604,571],{"class":88},[48,606,203],{"class":58},[48,608,248],{"class":88},[48,610,611,613,616,618,620,622,624,626,629,632,635,637],{"class":50,"line":156},[48,612,562],{"class":88},[48,614,615],{"class":58},"\"Leverage OK\"",[48,617,132],{"class":88},[48,619,111],{"class":84},[48,621,571],{"class":88},[48,623,524],{"class":58},[48,625,132],{"class":88},[48,627,628],{"class":84},"\u003C=",[48,630,631],{"class":191}," LIMITS",[48,633,634],{"class":88},"[",[48,636,524],{"class":58},[48,638,248],{"class":88},[48,640,641,643,646,648,650,652,654,656,659,661,663,665],{"class":50,"line":170},[48,642,562],{"class":88},[48,644,645],{"class":58},"\"Cover OK\"",[48,647,132],{"class":88},[48,649,111],{"class":84},[48,651,571],{"class":88},[48,653,535],{"class":58},[48,655,132],{"class":88},[48,657,658],{"class":84},">=",[48,660,631],{"class":191},[48,662,634],{"class":88},[48,664,535],{"class":58},[48,666,248],{"class":88},[48,668,669,671,674,676,678,680,682,684,686,688,690,692],{"class":50,"line":198},[48,670,562],{"class":88},[48,672,673],{"class":58},"\"Cash OK\"",[48,675,132],{"class":88},[48,677,111],{"class":84},[48,679,571],{"class":88},[48,681,245],{"class":58},[48,683,132],{"class":88},[48,685,658],{"class":84},[48,687,631],{"class":191},[48,689,634],{"class":88},[48,691,545],{"class":58},[48,693,248],{"class":88},[48,695,696],{"class":50,"line":221},[48,697,102],{"emptyLinePlaceholder":101},[48,699,700],{"class":50,"line":251},[48,701,703],{"class":702},"s-wDw","# headroom: how far LTM EBITDA (or cash) could fall before each test fails\n",[48,705,706,708,711,713,715,717,719,721,723,725,727,729,731,733,735,737],{"class":50,"line":256},[48,707,562],{"class":88},[48,709,710],{"class":58},"\"EBITDA headroom (leverage)\"",[48,712,132],{"class":88},[48,714,111],{"class":84},[48,716,571],{"class":88},[48,718,175],{"class":58},[48,720,132],{"class":88},[48,722,240],{"class":84},[48,724,571],{"class":88},[48,726,226],{"class":58},[48,728,132],{"class":88},[48,730,578],{"class":84},[48,732,631],{"class":191},[48,734,634],{"class":88},[48,736,524],{"class":58},[48,738,248],{"class":88},[48,740,741,743,746,748,750,752,754,756,758,760,762,764,767,769,771,773],{"class":50,"line":298},[48,742,562],{"class":88},[48,744,745],{"class":58},"\"EBITDA headroom (cover)\"",[48,747,132],{"class":88},[48,749,111],{"class":84},[48,751,571],{"class":88},[48,753,175],{"class":58},[48,755,132],{"class":88},[48,757,240],{"class":84},[48,759,571],{"class":88},[48,761,203],{"class":58},[48,763,132],{"class":88},[48,765,766],{"class":84},"*",[48,768,631],{"class":191},[48,770,634],{"class":88},[48,772,535],{"class":58},[48,774,248],{"class":88},[48,776,777,779,782,784,786,788,790,792,794,796,798,800],{"class":50,"line":331},[48,778,562],{"class":88},[48,780,781],{"class":58},"\"Cash headroom\"",[48,783,132],{"class":88},[48,785,111],{"class":84},[48,787,571],{"class":88},[48,789,245],{"class":58},[48,791,132],{"class":88},[48,793,240],{"class":84},[48,795,631],{"class":191},[48,797,634],{"class":88},[48,799,545],{"class":58},[48,801,248],{"class":88},[48,803,805,807,810,812,814,817,819,821,823,826,829,831,834,837,840,843,847,850,853,856,859,861,864,866],{"class":50,"line":804},13,[48,806,562],{"class":88},[48,808,809],{"class":58},"\"Tightest\"",[48,811,132],{"class":88},[48,813,111],{"class":84},[48,815,816],{"class":88}," tests[[",[48,818,710],{"class":58},[48,820,270],{"class":88},[48,822,745],{"class":58},[48,824,825],{"class":88},"]].idxmin(",[48,827,828],{"class":145},"axis",[48,830,111],{"class":84},[48,832,833],{"class":191},"1",[48,835,836],{"class":88},").str.extract(",[48,838,839],{"class":84},"r",[48,841,842],{"class":58},"\"",[48,844,846],{"class":845},"s_b0D","\\(",[48,848,849],{"class":191},"(\\w",[48,851,852],{"class":84},"+",[48,854,855],{"class":191},")",[48,857,858],{"class":845},"\\)",[48,860,842],{"class":58},[48,862,863],{"class":88},")[",[48,865,340],{"class":191},[48,867,248],{"class":88},[48,869,871,874,876,879,881,883,885,887,889,891,893,895,897,899,901,903,905],{"class":50,"line":870},14,[48,872,873],{"class":88},"show ",[48,875,111],{"class":84},[48,877,878],{"class":88}," [",[48,880,309],{"class":58},[48,882,270],{"class":88},[48,884,524],{"class":58},[48,886,270],{"class":88},[48,888,535],{"class":58},[48,890,270],{"class":88},[48,892,245],{"class":58},[48,894,270],{"class":88},[48,896,710],{"class":58},[48,898,270],{"class":88},[48,900,745],{"class":58},[48,902,270],{"class":88},[48,904,809],{"class":58},[48,906,248],{"class":88},[48,908,910,912,915,918],{"class":50,"line":909},15,[48,911,334],{"class":191},[48,913,914],{"class":88},"(tests[show].round(",[48,916,917],{"class":191},"2",[48,919,343],{"class":88},[10,921,922,923,926,927,930,931,934],{},"Each test is a comparison of a ratio or balance with its limit, and the pass\u002Ffail columns are what goes on the certificate. Headroom is more useful to management, and the clearest way to express it is in money: for leverage, the EBITDA at which net debt divided by EBITDA would equal 3.0× is net debt ÷ 3.0, so the headroom is current LTM EBITDA minus that figure; for interest cover, the floor is LTM interest × 4.0. Expressed this way, ",[14,924,925],{},"EBITDA could fall by £344,000 before leverage breaches"," is a sentence a board understands immediately, while ",[14,928,929],{},"2.78× against 3.0×"," needs explaining. The ",[14,932,933],{},"tightest"," column names the covenant with the least headroom at each date — usually leverage for a business with substantial debt.",[10,936,937],{},"In this example every test passes at every date, but leverage is clearly the covenant to watch. A weak second quarter in 2026 pulls twelve-month EBITDA down just as a cash outflow in June raises net debt, so leverage rises from 2.55× in March to 2.78× in June and the EBITDA headroom more than halves, from £746,000 to £344,000 — a fall of 7% in twelve-month earnings would breach the covenant. Interest cover, at 4.73× against a minimum of 4.0×, still has over £700,000 of headroom, and cash never comes near its £750,000 floor. The forecast quarters show leverage easing back to 2.57× by March 2027 as debt is repaid and earnings recover.",[348,939,357,944,357,947,357,950,357,953,357,958,357,964,357,970,357,975,357,979,357,983,357,986,357,991,357,995,357,998,357,1001],{"viewBox":940,"role":351,"ariaLabelledBy":941,"xmlns":355,"style":356},"4 0 752 173",[942,943],"cov-headroom-t","cov-headroom-d",[359,945,946],{"id":942},"Ratio or headroom",[363,948,949],{"id":943},"A ratio such as 2.4 times against a 3.0 times limit is what the loan agreement tests and the certificate reports. Headroom expressed in pounds, such as EBITDA could fall by 900,000 before a breach, is what management and the board can act on.",[367,951],{"x":369,"y":340,"width":370,"height":952,"fill":372},"173",[367,954],{"x":375,"y":376,"width":955,"height":956,"rx":957,"fill":379,"stroke":380,"style":381},"351.0","141.0","14",[383,959,963],{"x":960,"y":961,"style":962},"195.5","44.0","font-size:14px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","The ratio",[50,965],{"x1":966,"y1":967,"x2":968,"y2":967,"stroke":380,"style":969},"36.0","57.0","355.0","stroke-width:1px",[383,971,974],{"x":960,"y":972,"style":973},"77.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","what the agreement tests",[383,976,978],{"x":960,"y":977,"style":973},"103.0","goes on the certificate",[383,980,982],{"x":960,"y":981,"style":973},"129.0","2.4× against 3.0×",[367,984],{"x":985,"y":376,"width":955,"height":956,"rx":957,"fill":492,"stroke":493,"style":381},"389.0",[383,987,990],{"x":988,"y":961,"style":989},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Headroom in pounds",[50,992],{"x1":993,"y1":967,"x2":994,"y2":967,"stroke":493,"style":969},"405.0","724.0",[383,996,997],{"x":988,"y":972,"style":973},"what the board acts on",[383,999,1000],{"x":988,"y":977,"style":973},"EBITDA or cash cushion",[383,1002,1003],{"x":988,"y":981,"style":973},"names the tightest test",[33,1005,1007],{"id":1006},"step-3-look-ahead-with-the-forecast","Step 3 — Look ahead with the forecast",[38,1009,1011],{"className":75,"code":1010,"language":77,"meta":43,"style":43},"ahead = tests[tests[\"Type\"] == \"forecast\"][[\"Leverage\", \"Interest cover\", \"Cash\", \"EBITDA headroom (leverage)\"]]\nprint(ahead.round(2).to_string())\nlowest = tests[\"EBITDA headroom (leverage)\"].idxmin()\nprint(f\"lowest leverage headroom: £{tests.loc[lowest, 'EBITDA headroom (leverage)']:,.0f} at {lowest}\")\n",[45,1012,1013,1053,1064,1078],{"__ignoreMap":43},[48,1014,1015,1018,1020,1023,1025,1027,1030,1033,1036,1038,1040,1042,1044,1046,1048,1050],{"class":50,"line":51},[48,1016,1017],{"class":88},"ahead ",[48,1019,111],{"class":84},[48,1021,1022],{"class":88}," tests[tests[",[48,1024,309],{"class":58},[48,1026,132],{"class":88},[48,1028,1029],{"class":84},"==",[48,1031,1032],{"class":58}," \"forecast\"",[48,1034,1035],{"class":88},"][[",[48,1037,524],{"class":58},[48,1039,270],{"class":88},[48,1041,535],{"class":58},[48,1043,270],{"class":88},[48,1045,245],{"class":58},[48,1047,270],{"class":88},[48,1049,710],{"class":58},[48,1051,1052],{"class":88},"]]\n",[48,1054,1055,1057,1060,1062],{"class":50,"line":98},[48,1056,334],{"class":191},[48,1058,1059],{"class":88},"(ahead.round(",[48,1061,917],{"class":191},[48,1063,343],{"class":88},[48,1065,1066,1069,1071,1073,1075],{"class":50,"line":105},[48,1067,1068],{"class":88},"lowest ",[48,1070,111],{"class":84},[48,1072,571],{"class":88},[48,1074,710],{"class":58},[48,1076,1077],{"class":88},"].idxmin()\n",[48,1079,1080,1082,1085,1088,1091,1095,1098,1101,1104,1107,1110,1113,1115,1118,1120,1122],{"class":50,"line":123},[48,1081,334],{"class":191},[48,1083,1084],{"class":88},"(",[48,1086,1087],{"class":84},"f",[48,1089,1090],{"class":58},"\"lowest leverage headroom: £",[48,1092,1094],{"class":1093},"sSjpA","{",[48,1096,1097],{"class":88},"tests.loc[lowest, ",[48,1099,1100],{"class":58},"'EBITDA headroom (leverage)'",[48,1102,1103],{"class":88},"]",[48,1105,1106],{"class":84},":,.0f",[48,1108,1109],{"class":1093},"}",[48,1111,1112],{"class":58}," at ",[48,1114,1094],{"class":1093},[48,1116,1117],{"class":88},"lowest",[48,1119,1109],{"class":1093},[48,1121,842],{"class":58},[48,1123,120],{"class":88},[10,1125,1126],{},"Because the forecast months are in the same table, the same calculation gives the covenant position at the next two test dates. That is the point of automating the report rather than calculating each quarter by hand: a forecast that shows headroom shrinking towards zero gives months of warning, which is enough time to talk to the bank, change the timing of a capital project or draw on a facility — instead of discovering a breach when the quarter's accounts are closed. A sensitivity on top — rerunning the tests with forecast EBITDA reduced by 10% or 20% — is a two-line change and shows how much bad news the business can absorb. Here the December forecast has £500,000 of leverage headroom on twelve-month EBITDA of £4.7 million, so a 10% shortfall in the coming quarter alone would be absorbed, but a repeat of the second quarter's weakness would bring the test very close to its limit.",[33,1128,1130],{"id":1129},"step-4-a-workbook-with-visible-formulas","Step 4 — A workbook with visible formulas",[38,1132,1134],{"className":75,"code":1133,"language":77,"meta":43,"style":43},"from openpyxl import Workbook\nfrom openpyxl.styles import Font\n\nwb = Workbook()\nws = wb.active\nws.title = \"Covenants\"\nws.append([\"Test date\", \"Type\", \"LTM EBITDA\", \"LTM interest\", \"Net debt\", \"Cash\", \"Leverage\", \"Limit\", \"Interest cover\", \"Limit\", \"Minimum cash\"])\nfor c in ws[1]:\n    c.font = Font(bold=True)\nfor r, (period, row) in enumerate(tests.iterrows(), start=2):\n    ws.append([str(period), row[\"Type\"], round(row[\"LTM EBITDA\"]), round(row[\"LTM interest\"]), round(row[\"Net debt\"]), round(row[\"Cash\"]),\n               f\"=E{r}\u002FC{r}\", LIMITS[\"Leverage\"], f\"=C{r}\u002FD{r}\", LIMITS[\"Interest cover\"], LIMITS[\"Minimum cash\"]])\n    for col in \"CDEFK\":\n        ws[f\"{col}{r}\"].number_format = \"£#,##0\"\n    for col in \"GI\":\n        ws[f\"{col}{r}\"].number_format = '0.00\"×\"'\nwb.save(\"covenant_compliance.xlsx\")\nprint(\"rows:\", ws.max_row - 1)\n",[45,1135,1136,1149,1161,1165,1175,1185,1195,1247,1266,1286,1311,1362,1438,1454,1485,1498,1526,1537],{"__ignoreMap":43},[48,1137,1138,1141,1144,1146],{"class":50,"line":51},[48,1139,1140],{"class":84},"from",[48,1142,1143],{"class":88}," openpyxl ",[48,1145,85],{"class":84},[48,1147,1148],{"class":88}," Workbook\n",[48,1150,1151,1153,1156,1158],{"class":50,"line":98},[48,1152,1140],{"class":84},[48,1154,1155],{"class":88}," openpyxl.styles ",[48,1157,85],{"class":84},[48,1159,1160],{"class":88}," Font\n",[48,1162,1163],{"class":50,"line":105},[48,1164,102],{"emptyLinePlaceholder":101},[48,1166,1167,1170,1172],{"class":50,"line":123},[48,1168,1169],{"class":88},"wb ",[48,1171,111],{"class":84},[48,1173,1174],{"class":88}," Workbook()\n",[48,1176,1177,1180,1182],{"class":50,"line":156},[48,1178,1179],{"class":88},"ws ",[48,1181,111],{"class":84},[48,1183,1184],{"class":88}," wb.active\n",[48,1186,1187,1190,1192],{"class":50,"line":170},[48,1188,1189],{"class":88},"ws.title ",[48,1191,111],{"class":84},[48,1193,1194],{"class":58}," \"Covenants\"\n",[48,1196,1197,1200,1203,1205,1207,1209,1211,1213,1215,1217,1219,1221,1223,1225,1227,1229,1232,1234,1236,1238,1240,1242,1244],{"class":50,"line":198},[48,1198,1199],{"class":88},"ws.append([",[48,1201,1202],{"class":58},"\"Test date\"",[48,1204,270],{"class":88},[48,1206,309],{"class":58},[48,1208,270],{"class":88},[48,1210,175],{"class":58},[48,1212,270],{"class":88},[48,1214,203],{"class":58},[48,1216,270],{"class":88},[48,1218,226],{"class":58},[48,1220,270],{"class":88},[48,1222,245],{"class":58},[48,1224,270],{"class":88},[48,1226,524],{"class":58},[48,1228,270],{"class":88},[48,1230,1231],{"class":58},"\"Limit\"",[48,1233,270],{"class":88},[48,1235,535],{"class":58},[48,1237,270],{"class":88},[48,1239,1231],{"class":58},[48,1241,270],{"class":88},[48,1243,545],{"class":58},[48,1245,1246],{"class":88},"])\n",[48,1248,1249,1252,1255,1258,1261,1263],{"class":50,"line":221},[48,1250,1251],{"class":84},"for",[48,1253,1254],{"class":88}," c ",[48,1256,1257],{"class":84},"in",[48,1259,1260],{"class":88}," ws[",[48,1262,833],{"class":191},[48,1264,1265],{"class":88},"]:\n",[48,1267,1268,1271,1273,1276,1279,1281,1284],{"class":50,"line":251},[48,1269,1270],{"class":88},"    c.font ",[48,1272,111],{"class":84},[48,1274,1275],{"class":88}," Font(",[48,1277,1278],{"class":145},"bold",[48,1280,111],{"class":84},[48,1282,1283],{"class":191},"True",[48,1285,120],{"class":88},[48,1287,1288,1290,1293,1295,1298,1301,1304,1306,1308],{"class":50,"line":256},[48,1289,1251],{"class":84},[48,1291,1292],{"class":88}," r, (period, row) ",[48,1294,1257],{"class":84},[48,1296,1297],{"class":191}," enumerate",[48,1299,1300],{"class":88},"(tests.iterrows(), ",[48,1302,1303],{"class":145},"start",[48,1305,111],{"class":84},[48,1307,917],{"class":191},[48,1309,1310],{"class":88},"):\n",[48,1312,1313,1316,1319,1322,1324,1326,1329,1332,1334,1337,1339,1341,1343,1345,1347,1349,1351,1353,1355,1357,1359],{"class":50,"line":298},[48,1314,1315],{"class":88},"    ws.append([",[48,1317,1318],{"class":191},"str",[48,1320,1321],{"class":88},"(period), row[",[48,1323,309],{"class":58},[48,1325,142],{"class":88},[48,1327,1328],{"class":191},"round",[48,1330,1331],{"class":88},"(row[",[48,1333,175],{"class":58},[48,1335,1336],{"class":88},"]), ",[48,1338,1328],{"class":191},[48,1340,1331],{"class":88},[48,1342,203],{"class":58},[48,1344,1336],{"class":88},[48,1346,1328],{"class":191},[48,1348,1331],{"class":88},[48,1350,226],{"class":58},[48,1352,1336],{"class":88},[48,1354,1328],{"class":191},[48,1356,1331],{"class":88},[48,1358,245],{"class":58},[48,1360,1361],{"class":88},"]),\n",[48,1363,1364,1367,1370,1372,1374,1376,1379,1381,1383,1385,1387,1389,1391,1393,1395,1397,1399,1402,1404,1406,1408,1411,1413,1415,1417,1419,1421,1423,1425,1427,1429,1431,1433,1435],{"class":50,"line":331},[48,1365,1366],{"class":84},"               f",[48,1368,1369],{"class":58},"\"=E",[48,1371,1094],{"class":1093},[48,1373,839],{"class":88},[48,1375,1109],{"class":1093},[48,1377,1378],{"class":58},"\u002FC",[48,1380,1094],{"class":1093},[48,1382,839],{"class":88},[48,1384,1109],{"class":1093},[48,1386,842],{"class":58},[48,1388,270],{"class":88},[48,1390,515],{"class":191},[48,1392,634],{"class":88},[48,1394,524],{"class":58},[48,1396,142],{"class":88},[48,1398,1087],{"class":84},[48,1400,1401],{"class":58},"\"=C",[48,1403,1094],{"class":1093},[48,1405,839],{"class":88},[48,1407,1109],{"class":1093},[48,1409,1410],{"class":58},"\u002FD",[48,1412,1094],{"class":1093},[48,1414,839],{"class":88},[48,1416,1109],{"class":1093},[48,1418,842],{"class":58},[48,1420,270],{"class":88},[48,1422,515],{"class":191},[48,1424,634],{"class":88},[48,1426,535],{"class":58},[48,1428,142],{"class":88},[48,1430,515],{"class":191},[48,1432,634],{"class":88},[48,1434,545],{"class":58},[48,1436,1437],{"class":88},"]])\n",[48,1439,1440,1443,1446,1448,1451],{"class":50,"line":804},[48,1441,1442],{"class":84},"    for",[48,1444,1445],{"class":88}," col ",[48,1447,1257],{"class":84},[48,1449,1450],{"class":58}," \"CDEFK\"",[48,1452,1453],{"class":88},":\n",[48,1455,1456,1459,1461,1463,1465,1468,1471,1473,1475,1477,1480,1482],{"class":50,"line":870},[48,1457,1458],{"class":88},"        ws[",[48,1460,1087],{"class":84},[48,1462,842],{"class":58},[48,1464,1094],{"class":1093},[48,1466,1467],{"class":88},"col",[48,1469,1470],{"class":1093},"}{",[48,1472,839],{"class":88},[48,1474,1109],{"class":1093},[48,1476,842],{"class":58},[48,1478,1479],{"class":88},"].number_format ",[48,1481,111],{"class":84},[48,1483,1484],{"class":58}," \"£#,##0\"\n",[48,1486,1487,1489,1491,1493,1496],{"class":50,"line":909},[48,1488,1442],{"class":84},[48,1490,1445],{"class":88},[48,1492,1257],{"class":84},[48,1494,1495],{"class":58}," \"GI\"",[48,1497,1453],{"class":88},[48,1499,1501,1503,1505,1507,1509,1511,1513,1515,1517,1519,1521,1523],{"class":50,"line":1500},16,[48,1502,1458],{"class":88},[48,1504,1087],{"class":84},[48,1506,842],{"class":58},[48,1508,1094],{"class":1093},[48,1510,1467],{"class":88},[48,1512,1470],{"class":1093},[48,1514,839],{"class":88},[48,1516,1109],{"class":1093},[48,1518,842],{"class":58},[48,1520,1479],{"class":88},[48,1522,111],{"class":84},[48,1524,1525],{"class":58}," '0.00\"×\"'\n",[48,1527,1529,1532,1535],{"class":50,"line":1528},17,[48,1530,1531],{"class":88},"wb.save(",[48,1533,1534],{"class":58},"\"covenant_compliance.xlsx\"",[48,1536,120],{"class":88},[48,1538,1540,1542,1544,1547,1550,1552,1555],{"class":50,"line":1539},18,[48,1541,334],{"class":191},[48,1543,1084],{"class":88},[48,1545,1546],{"class":58},"\"rows:\"",[48,1548,1549],{"class":88},", ws.max_row ",[48,1551,240],{"class":84},[48,1553,1554],{"class":191}," 1",[48,1556,120],{"class":88},[10,1558,1559,1560,1563],{},"The workbook that goes with a compliance certificate should let the lender check the arithmetic, so the inputs — LTM EBITDA, LTM interest, net debt and cash — are written as values and the ratios as formulas that divide them, with the limits alongside. Anyone opening it can click a ratio and see exactly how it was calculated, which saves a round of questions every quarter and makes the certificate easy to sign off. The ratios use a custom number format that shows ",[14,1561,1562],{},"2.40×",", the way covenants are usually quoted. A second sheet with the monthly data behind the twelve-month sums completes the audit trail.",[348,1565,357,1571,357,1574,357,1577,357,1582,357,1587,357,1591,357,1597,357,1601,357,1605,357,1609,357,1613,357,1617,357,1621,357,1625,357,1629,357,1633,357,1637,357,1640,357,1644,357,1648,357,1651,357,1654,357,1658,357,1660,357,1663,357,1665,357,1667,357,1669,357,1672,357,1674,357,1677,357,1679,357,1682,357,1684,357,1686,357,1689,357,1692,357,1694,357,1697,357,1699,357,1702,357,1704,357,1706,357,1708,357,1712,357,1714,357,1716,357,1718,357,1721,357,1723,357,1725,357,1727,357,1730,357,1732,357,1735,357,1737,357,1739,357,1741,357,1743,357,1745,357,1748,357,1750,357,1752,357,1754,357,1757,357,1759,357,1761,357,1764,357,1767,357,1769,357,1772,357,1774,357,1776,357,1778,357,1780,357,1782],{"viewBox":1566,"role":351,"ariaLabelledBy":1567,"xmlns":355,"style":1570},"68 0 624 168",[1568,1569],"cov-sheet-t","cov-sheet-d","width:100%;max-width:624px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[359,1572,1573],{"id":1568},"The covenant sheet",[363,1575,1576],{"id":1569},"Each test date row holds LTM EBITDA, LTM interest, net debt and cash as values, the leverage and interest cover as formulas dividing those values, and the limits beside them so the lender can check each test.",[367,1578],{"x":1579,"y":340,"width":1580,"height":1581,"fill":372},"68","624","168",[367,1583],{"x":1584,"y":376,"width":1585,"height":1586,"rx":340,"fill":379,"stroke":380,"style":969},"84.4","32.0","24.0",[367,1588],{"x":1589,"y":376,"width":1590,"height":1586,"rx":340,"fill":379,"stroke":380,"style":969},"116.4","81.9",[383,1592,1596],{"x":1593,"y":1594,"style":1595},"157.4","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[367,1598],{"x":1599,"y":376,"width":1600,"height":1586,"rx":340,"fill":379,"stroke":380,"style":969},"198.3","100.7",[383,1602,1604],{"x":1603,"y":1594,"style":1595},"248.7","B",[367,1606],{"x":1607,"y":376,"width":1608,"height":1586,"rx":340,"fill":379,"stroke":380,"style":969},"299.0","76.7",[383,1610,1612],{"x":1611,"y":1594,"style":1595},"337.4","C",[367,1614],{"x":1615,"y":376,"width":1616,"height":1586,"rx":340,"fill":379,"stroke":380,"style":969},"375.7","81.6",[383,1618,1620],{"x":1619,"y":1594,"style":1595},"416.5","D",[367,1622],{"x":1623,"y":376,"width":1624,"height":1586,"rx":340,"fill":379,"stroke":380,"style":969},"457.4","54.1",[383,1626,1628],{"x":1627,"y":1594,"style":1595},"484.4","E",[367,1630],{"x":1631,"y":376,"width":1632,"height":1586,"rx":340,"fill":379,"stroke":380,"style":969},"511.4","110.1",[383,1634,1636],{"x":1635,"y":1594,"style":1595},"566.5","F",[367,1638],{"x":1639,"y":376,"width":1624,"height":1586,"rx":340,"fill":379,"stroke":380,"style":969},"621.6",[383,1641,1643],{"x":1642,"y":1594,"style":1595},"648.6","G",[367,1645],{"x":1584,"y":1646,"width":1585,"height":1647,"rx":340,"fill":379,"stroke":380,"style":969},"40.0","28.0",[383,1649,833],{"x":1650,"y":391,"style":1595},"100.4",[367,1652],{"x":1589,"y":1646,"width":1590,"height":1647,"rx":340,"fill":420,"stroke":411,"style":1653},"stroke-width:1.5px",[383,1655,1657],{"x":1593,"y":391,"style":1656},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Test date",[367,1659],{"x":1599,"y":1646,"width":1600,"height":1647,"rx":340,"fill":420,"stroke":411,"style":1653},[383,1661,1662],{"x":1603,"y":391,"style":1656},"LTM EBITDA",[367,1664],{"x":1607,"y":1646,"width":1608,"height":1647,"rx":340,"fill":420,"stroke":411,"style":1653},[383,1666,445],{"x":1611,"y":391,"style":1656},[367,1668],{"x":1615,"y":1646,"width":1616,"height":1647,"rx":340,"fill":420,"stroke":411,"style":1653},[383,1670,1671],{"x":1619,"y":391,"style":1656},"Leverage",[367,1673],{"x":1623,"y":1646,"width":1624,"height":1647,"rx":340,"fill":420,"stroke":411,"style":1653},[383,1675,1676],{"x":1627,"y":391,"style":1656},"Limit",[367,1678],{"x":1631,"y":1646,"width":1632,"height":1647,"rx":340,"fill":420,"stroke":411,"style":1653},[383,1680,1681],{"x":1635,"y":391,"style":1656},"Interest cover",[367,1683],{"x":1639,"y":1646,"width":1624,"height":1647,"rx":340,"fill":420,"stroke":411,"style":1653},[383,1685,1676],{"x":1642,"y":391,"style":1656},[367,1687],{"x":1584,"y":1688,"width":1585,"height":1647,"rx":340,"fill":379,"stroke":380,"style":969},"68.0",[383,1690,917],{"x":1650,"y":1691,"style":1595},"87.0",[367,1693],{"x":1589,"y":1688,"width":1590,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1695,1696],{"x":1593,"y":1691,"style":973},"2025-12",[367,1698],{"x":1599,"y":1688,"width":1600,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1700,1701],{"x":1603,"y":1691,"style":973},"value",[367,1703],{"x":1607,"y":1688,"width":1608,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1705,1701],{"x":1611,"y":1691,"style":973},[367,1707],{"x":1615,"y":1688,"width":1616,"height":1647,"rx":340,"fill":492,"stroke":493,"style":1653},[383,1709,1711],{"x":1619,"y":1691,"style":1710},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","=E2\u002FC2",[367,1713],{"x":1623,"y":1688,"width":1624,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1715,530],{"x":1627,"y":1691,"style":973},[367,1717],{"x":1631,"y":1688,"width":1632,"height":1647,"rx":340,"fill":492,"stroke":493,"style":1653},[383,1719,1720],{"x":1635,"y":1691,"style":1710},"=C2\u002FD2",[367,1722],{"x":1639,"y":1688,"width":1624,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1724,540],{"x":1642,"y":1691,"style":973},[367,1726],{"x":1584,"y":400,"width":1585,"height":1647,"rx":340,"fill":379,"stroke":380,"style":969},[383,1728,267],{"x":1650,"y":1729,"style":1595},"115.0",[367,1731],{"x":1589,"y":400,"width":1590,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1733,1734],{"x":1593,"y":1729,"style":973},"2026-03",[367,1736],{"x":1599,"y":400,"width":1600,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1738,1701],{"x":1603,"y":1729,"style":973},[367,1740],{"x":1607,"y":400,"width":1608,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1742,1701],{"x":1611,"y":1729,"style":973},[367,1744],{"x":1615,"y":400,"width":1616,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1746,1747],{"x":1619,"y":1729,"style":973},"=E3\u002FC3",[367,1749],{"x":1623,"y":400,"width":1624,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1751,530],{"x":1627,"y":1729,"style":973},[367,1753],{"x":1631,"y":400,"width":1632,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1755,1756],{"x":1635,"y":1729,"style":973},"=C3\u002FD3",[367,1758],{"x":1639,"y":400,"width":1624,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1760,540],{"x":1642,"y":1729,"style":973},[367,1762],{"x":1584,"y":1763,"width":1585,"height":1647,"rx":340,"fill":379,"stroke":380,"style":969},"124.0",[383,1765,369],{"x":1650,"y":1766,"style":1595},"143.0",[367,1768],{"x":1589,"y":1763,"width":1590,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[383,1770,1771],{"x":1593,"y":1766,"style":973},"…",[367,1773],{"x":1599,"y":1763,"width":1600,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[367,1775],{"x":1607,"y":1763,"width":1608,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[367,1777],{"x":1615,"y":1763,"width":1616,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[367,1779],{"x":1623,"y":1763,"width":1624,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[367,1781],{"x":1631,"y":1763,"width":1632,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[367,1783],{"x":1639,"y":1763,"width":1624,"height":1647,"rx":340,"fill":372,"stroke":380,"style":969},[33,1785,1787],{"id":1786},"common-pitfalls","Common pitfalls",[1789,1790,1791,1799,1805,1811,1817],"ul",{},[1792,1793,1794,1798],"li",{},[1795,1796,1797],"strong",{},"Quarterly EBITDA instead of twelve months."," Covenants use rolling LTM figures.",[1792,1800,1801,1804],{},[1795,1802,1803],{},"Accounting definitions instead of the agreement's."," Apply the agreement's adjustments explicitly.",[1792,1806,1807,1810],{},[1795,1808,1809],{},"Reporting only pass or fail."," Show headroom in money and the tightest covenant.",[1792,1812,1813,1816],{},[1795,1814,1815],{},"Testing only the past."," Run the same tests on the forecast for early warning.",[1792,1818,1819,1822],{},[1795,1820,1821],{},"Ratios as typed numbers."," Write them as formulas so the lender can check them.",[33,1824,1826],{"id":1825},"conclusion","Conclusion",[10,1828,1829],{},"A covenant report takes monthly accounts, builds rolling twelve-month EBITDA and interest, measures net debt and cash at each quarter end, and compares leverage, interest cover and cash with the limits in the loan agreement. Headroom expressed as how far EBITDA or cash could fall before a breach, the tightest covenant at each date, and the same tests run on the forecast give management early warning, and a workbook with the ratios as live formulas gives the lender a checkable certificate.",[33,1831,1833],{"id":1832},"frequently-asked-questions","Frequently asked questions",[10,1835,1836,1839,1840,31],{},[1795,1837,1838],{},"How do I calculate LTM EBITDA in pandas?","\nUse a rolling twelve-month sum over monthly EBITDA: ",[45,1841,1842],{},"df[\"EBITDA\"].rolling(12).sum()",[10,1844,1845,1848],{},[1795,1846,1847],{},"How is leverage calculated for a covenant?","\nUsually net debt — borrowing less cash — divided by last-twelve-month EBITDA, using the loan agreement's definitions.",[10,1850,1851,1854],{},[1795,1852,1853],{},"How do I express covenant headroom?","\nAs the amount EBITDA could fall before the ratio reaches its limit: LTM EBITDA minus net debt divided by the leverage limit.",[10,1856,1857,1860],{},[1795,1858,1859],{},"Why include the forecast in a covenant report?","\nIt shows whether a breach is coming at the next test dates, while there is still time to act.",[33,1862,1864],{"id":1863},"related","Related",[1789,1866,1867,1873,1880,1887],{},[1792,1868,1869,1870,1872],{},"Up one level: ",[27,1871,30],{"href":29}," — finance reports from Python.",[1792,1874,1875,1879],{},[27,1876,1878],{"href":1877},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-amortization-schedule-in-excel\u002F","Build a Loan Amortization Schedule in Excel with Python"," — the debt side.",[1792,1881,1882,1886],{},[27,1883,1885],{"href":1884},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-cash-flow-forecast-report\u002F","Build a Cash Flow Forecast Report"," — where the forecast comes from.",[1792,1888,1889,1893],{},[27,1890,1892],{"href":1891},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Frunning-totals-and-year-over-year-growth-in-pandas\u002F","Running Totals and Year-Over-Year Growth in pandas"," — rolling sums.",[1895,1896,1897],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":43,"searchDepth":98,"depth":98,"links":1899},[1900,1901,1902,1903,1904,1905,1906,1907,1908],{"id":35,"depth":98,"text":36},{"id":71,"depth":98,"text":72},{"id":504,"depth":98,"text":505},{"id":1006,"depth":98,"text":1007},{"id":1129,"depth":98,"text":1130},{"id":1786,"depth":98,"text":1787},{"id":1825,"depth":98,"text":1826},{"id":1832,"depth":98,"text":1833},{"id":1863,"depth":98,"text":1864},"2026-10-02","Test bank loan covenants every quarter with pandas: last-twelve-month EBITDA and interest from monthly accounts, net debt at the quarter end, leverage, interest cover and minimum liquidity against their limits, the headroom on each covenant expressed as how far EBITDA or cash could fall before a breach, a forward look using the forecast, and a compliance workbook that shows its formulas.","md",[1913,1915,1917,1919],{"q":1838,"a":1914},"Use a rolling twelve-month sum over monthly EBITDA: df[\"EBITDA\"].rolling(12).sum().",{"q":1847,"a":1916},"Usually net debt — borrowing less cash — divided by last-twelve-month EBITDA, using the loan agreement's definitions.",{"q":1853,"a":1918},"As the amount EBITDA could fall before the ratio reaches its limit: LTM EBITDA minus net debt divided by the leverage limit.",{"q":1859,"a":1920},"It shows whether a breach is coming at the next test dates, while there is still time to act.","loan covenant compliance report python",null,{"breadcrumb":1924},[1925,1927,1930],{"name":1926,"item":578},"Home",{"name":1928,"item":1929},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":1931,"item":29},"Building Finance and Operations Reports in Excel with Python","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-covenant-compliance-report",{"title":1934,"description":1935},"Build a Loan Covenant Compliance Report with Python","Quarterly covenant tests with pandas: LTM EBITDA, net debt, leverage and interest cover against limits, liquidity, headroom as EBITDA and cash cushions, a forecast look-ahead and an Excel report with formulas.","build-a-loan-covenant-compliance-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-covenant-compliance-report\u002Findex",[1939,1940],"report-layouts","excel-formulas","how-to","5_Aeo0DJqTL9mTADDh_rNmgWZaqH2m1jnouCoyUSeUw",[1944,1947],{"title":1878,"path":1945,"stem":1946,"children":-1},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-amortization-schedule-in-excel","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-loan-amortization-schedule-in-excel\u002Findex",{"title":1948,"path":1949,"stem":1950,"children":-1},"Build a Maintenance Schedule in Excel","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-maintenance-schedule-in-excel","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-maintenance-schedule-in-excel\u002Findex",[1952,1954],{"path":1953,"title":1931},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports",{"path":1955,"title":1928},"\u002Fautomating-reporting-workflows",[],[1958,1962,1965,1968,1971,1974],{"path":1959,"title":1960,"kind":1961},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards","Building Multi-Sheet Excel Dashboards","guide",{"path":1963,"title":1964,"kind":1941},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-data-dictionary-sheet-to-an-excel-report","Add a Data Dictionary Sheet to an Excel Report",{"path":1966,"title":1967,"kind":1941},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-kpi-summary-block-to-an-excel-dashboard","Add a KPI Summary Block to an Excel Dashboard",{"path":1969,"title":1970,"kind":1941},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel","Add a Table of Contents Sheet with Hyperlinks in Excel",{"path":1972,"title":1973,"kind":1941},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard","Add a Dropdown Filter That Drives an Excel Dashboard",{"path":1975,"title":1976,"kind":1941},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python","Add a Summary Sheet to an Excel Report with Python",1790972121689]