[{"data":1,"prerenderedAt":3147},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python":3113,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python":3122,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python":3127,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python":3128},{"id":4,"title":5,"body":6,"dateModified":3080,"datePublished":3080,"description":3081,"extension":3082,"faq":3083,"keyword":3092,"kind":3093,"meta":3094,"navigation":249,"path":3102,"seo":3103,"slug":3106,"stem":3107,"topics":3108,"type":3111,"__hash__":3112},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python\u002Findex.md","YEARFRAC and DAYS360 Equivalents in Python",{"type":7,"value":8,"toc":3067},"minimark",[9,46,51,79,86,90,96,188,202,206,213,649,656,758,762,776,1169,1184,1188,1191,1552,1558,1698,1702,1705,2423,2433,2437,2440,2743,2754,2947,2951,2989,2993,3002,3006,3012,3018,3024,3030,3034,3063],[10,11,12,13,17,18,21,22,26,27,29,30,33,34,36,37,39,40,45],"p",{},"Interest accruals, bond coupons, pro-rata fees and leases in Excel often rely on ",[14,15,16],"code",{},"YEARFRAC"," and ",[14,19,20],{},"DAYS360",". Both look simple — the fraction of a year between two dates, the days between two dates on a 360-day calendar — but each hides a ",[23,24,25],"em",{},"day-count convention"," with specific rules for month ends, February and leap years. Moving such a workbook to Python means reproducing those rules exactly, or the figures drift by a few days' interest on exactly the dates where people check them: the end of February and months with 31 days. Python's standard library has no ",[14,28,16],{},", and naive ",[14,31,32],{},"(end - start).days \u002F 365"," matches only one of the five bases. This page implements all five ",[14,35,16],{}," bases and both ",[14,38,20],{}," methods, applies them to an accrual table in pandas, and checks every result against a spreadsheet's own calculation. It belongs to ",[41,42,44],"a",{"href":43},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002F","Excel Formula Equivalents in pandas",".",[47,48,50],"h2",{"id":49},"prerequisites","Prerequisites",[52,53,58],"pre",{"className":54,"code":55,"language":56,"meta":57,"style":57},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,59,60],{"__ignoreMap":57},[61,62,65,69,73,76],"span",{"class":63,"line":64},"line",1,[61,66,68],{"class":67},"sMTad","pip",[61,70,72],{"class":71},"srMev"," install",[61,74,75],{"class":71}," pandas",[61,77,78],{"class":71}," openpyxl\n",[10,80,81,82,85],{},"The functions use only the standard library; pandas applies them to columns. The checks write the same formulas into a workbook and recalculate it with LibreOffice (",[14,83,84],{},"soffice","), whose results for these functions follow the conventions described below.",[47,87,89],{"id":88},"the-five-bases-and-two-methods","The five bases and two methods",[10,91,92,95],{},[14,93,94],{},"YEARFRAC(start, end, basis)"," divides a day count by a year length, and the basis picks both:",[97,98,99,118],"table",{},[100,101,102],"thead",{},[103,104,105,109,112,115],"tr",{},[106,107,108],"th",{},"Basis",[106,110,111],{},"Convention",[106,113,114],{},"Day count",[106,116,117],{},"Year",[119,120,121,136,150,162,175],"tbody",{},[103,122,123,127,130,133],{},[124,125,126],"td",{},"0 (default)",[124,128,129],{},"US (NASD) 30\u002F360",[124,131,132],{},"30-day months, US end-of-month rules",[124,134,135],{},"360",[103,137,138,141,144,147],{},[124,139,140],{},"1",[124,142,143],{},"actual\u002Factual",[124,145,146],{},"real days",[124,148,149],{},"365 or 366, by Excel's rule",[103,151,152,155,158,160],{},[124,153,154],{},"2",[124,156,157],{},"actual\u002F360",[124,159,146],{},[124,161,135],{},[103,163,164,167,170,172],{},[124,165,166],{},"3",[124,168,169],{},"actual\u002F365",[124,171,146],{},[124,173,174],{},"365",[103,176,177,180,183,186],{},[124,178,179],{},"4",[124,181,182],{},"European 30\u002F360",[124,184,185],{},"30-day months, 31st becomes 30th",[124,187,135],{},[10,189,190,193,194,197,198,201],{},[14,191,192],{},"DAYS360(start, end, method)"," returns the 30\u002F360 day count itself: the US method by default (",[14,195,196],{},"FALSE",") or the European method (",[14,199,200],{},"TRUE","). The 30\u002F360 rules all pretend every month has 30 days, so that monthly interest is the same every month; they differ only in how a 31st and the end of February are treated.",[47,203,205],{"id":204},"step-1-days360-us-and-european","Step 1 — DAYS360, US and European",[10,207,208,209,212],{},"Both methods compute ",[14,210,211],{},"360 × years + 30 × months + days"," from adjusted day numbers. The adjustments are the whole difference:",[52,214,218],{"className":215,"code":216,"language":217,"meta":57,"style":57},"language-python shiki shiki-themes github-light github-dark-high-contrast","import calendar\nfrom datetime import date\n\ndef is_month_end(d):\n    return d.day == calendar.monthrange(d.year, d.month)[1]\n\ndef days360(start, end, european=False):\n    d1, d2 = start.day, end.day\n    if european:\n        d1 = min(d1, 30)\n        d2 = min(d2, 30)\n    else:\n        if is_month_end(start):\n            d1 = 30\n        if d2 == 31 and d1 >= 30:\n            d2 = 30\n    return 360 * (end.year - start.year) + 30 * (end.month - start.month) + (d2 - d1)\n\nfor a, b in [(date(2024, 1, 31), date(2024, 3, 31)), (date(2025, 2, 28), date(2025, 3, 31)),\n             (date(2024, 2, 29), date(2025, 2, 28)), (date(2024, 2, 28), date(2024, 3, 31))]:\n    print(a, \"→\", b, \"US:\", days360(a, b), \"EU:\", days360(a, b, european=True))\n","python",[14,219,220,230,244,251,264,285,290,310,321,330,350,367,376,385,396,423,433,478,483,554,610],{"__ignoreMap":57},[61,221,222,226],{"class":63,"line":64},[61,223,225],{"class":224},"s-kum","import",[61,227,229],{"class":228},"skGVy"," calendar\n",[61,231,233,236,239,241],{"class":63,"line":232},2,[61,234,235],{"class":224},"from",[61,237,238],{"class":228}," datetime ",[61,240,225],{"class":224},[61,242,243],{"class":228}," date\n",[61,245,247],{"class":63,"line":246},3,[61,248,250],{"emptyLinePlaceholder":249},true,"\n",[61,252,254,257,261],{"class":63,"line":253},4,[61,255,256],{"class":224},"def",[61,258,260],{"class":259},"s_Opv"," is_month_end",[61,262,263],{"class":228},"(d):\n",[61,265,267,270,273,276,279,282],{"class":63,"line":266},5,[61,268,269],{"class":224},"    return",[61,271,272],{"class":228}," d.day ",[61,274,275],{"class":224},"==",[61,277,278],{"class":228}," calendar.monthrange(d.year, d.month)[",[61,280,140],{"class":281},"sP0c6",[61,283,284],{"class":228},"]\n",[61,286,288],{"class":63,"line":287},6,[61,289,250],{"emptyLinePlaceholder":249},[61,291,293,295,298,301,304,307],{"class":63,"line":292},7,[61,294,256],{"class":224},[61,296,297],{"class":259}," days360",[61,299,300],{"class":228},"(start, end, european",[61,302,303],{"class":224},"=",[61,305,306],{"class":281},"False",[61,308,309],{"class":228},"):\n",[61,311,313,316,318],{"class":63,"line":312},8,[61,314,315],{"class":228},"    d1, d2 ",[61,317,303],{"class":224},[61,319,320],{"class":228}," start.day, end.day\n",[61,322,324,327],{"class":63,"line":323},9,[61,325,326],{"class":224},"    if",[61,328,329],{"class":228}," european:\n",[61,331,333,336,338,341,344,347],{"class":63,"line":332},10,[61,334,335],{"class":228},"        d1 ",[61,337,303],{"class":224},[61,339,340],{"class":281}," min",[61,342,343],{"class":228},"(d1, ",[61,345,346],{"class":281},"30",[61,348,349],{"class":228},")\n",[61,351,353,356,358,360,363,365],{"class":63,"line":352},11,[61,354,355],{"class":228},"        d2 ",[61,357,303],{"class":224},[61,359,340],{"class":281},[61,361,362],{"class":228},"(d2, ",[61,364,346],{"class":281},[61,366,349],{"class":228},[61,368,370,373],{"class":63,"line":369},12,[61,371,372],{"class":224},"    else",[61,374,375],{"class":228},":\n",[61,377,379,382],{"class":63,"line":378},13,[61,380,381],{"class":224},"        if",[61,383,384],{"class":228}," is_month_end(start):\n",[61,386,388,391,393],{"class":63,"line":387},14,[61,389,390],{"class":228},"            d1 ",[61,392,303],{"class":224},[61,394,395],{"class":281}," 30\n",[61,397,399,401,404,406,409,412,415,418,421],{"class":63,"line":398},15,[61,400,381],{"class":224},[61,402,403],{"class":228}," d2 ",[61,405,275],{"class":224},[61,407,408],{"class":281}," 31",[61,410,411],{"class":224}," and",[61,413,414],{"class":228}," d1 ",[61,416,417],{"class":224},">=",[61,419,420],{"class":281}," 30",[61,422,375],{"class":228},[61,424,426,429,431],{"class":63,"line":425},16,[61,427,428],{"class":228},"            d2 ",[61,430,303],{"class":224},[61,432,395],{"class":281},[61,434,436,438,441,444,447,450,453,456,458,460,463,465,468,470,473,475],{"class":63,"line":435},17,[61,437,269],{"class":224},[61,439,440],{"class":281}," 360",[61,442,443],{"class":224}," *",[61,445,446],{"class":228}," (end.year ",[61,448,449],{"class":224},"-",[61,451,452],{"class":228}," start.year) ",[61,454,455],{"class":224},"+",[61,457,420],{"class":281},[61,459,443],{"class":224},[61,461,462],{"class":228}," (end.month ",[61,464,449],{"class":224},[61,466,467],{"class":228}," start.month) ",[61,469,455],{"class":224},[61,471,472],{"class":228}," (d2 ",[61,474,449],{"class":224},[61,476,477],{"class":228}," d1)\n",[61,479,481],{"class":63,"line":480},18,[61,482,250],{"emptyLinePlaceholder":249},[61,484,486,489,492,495,498,501,504,506,508,511,514,516,518,520,522,524,527,530,532,534,536,539,541,543,545,547,549,551],{"class":63,"line":485},19,[61,487,488],{"class":224},"for",[61,490,491],{"class":228}," a, b ",[61,493,494],{"class":224},"in",[61,496,497],{"class":228}," [(date(",[61,499,500],{"class":281},"2024",[61,502,503],{"class":228},", ",[61,505,140],{"class":281},[61,507,503],{"class":228},[61,509,510],{"class":281},"31",[61,512,513],{"class":228},"), date(",[61,515,500],{"class":281},[61,517,503],{"class":228},[61,519,166],{"class":281},[61,521,503],{"class":228},[61,523,510],{"class":281},[61,525,526],{"class":228},")), (date(",[61,528,529],{"class":281},"2025",[61,531,503],{"class":228},[61,533,154],{"class":281},[61,535,503],{"class":228},[61,537,538],{"class":281},"28",[61,540,513],{"class":228},[61,542,529],{"class":281},[61,544,503],{"class":228},[61,546,166],{"class":281},[61,548,503],{"class":228},[61,550,510],{"class":281},[61,552,553],{"class":228},")),\n",[61,555,557,560,562,564,566,568,571,573,575,577,579,581,583,585,587,589,591,593,595,597,599,601,603,605,607],{"class":63,"line":556},20,[61,558,559],{"class":228},"             (date(",[61,561,500],{"class":281},[61,563,503],{"class":228},[61,565,154],{"class":281},[61,567,503],{"class":228},[61,569,570],{"class":281},"29",[61,572,513],{"class":228},[61,574,529],{"class":281},[61,576,503],{"class":228},[61,578,154],{"class":281},[61,580,503],{"class":228},[61,582,538],{"class":281},[61,584,526],{"class":228},[61,586,500],{"class":281},[61,588,503],{"class":228},[61,590,154],{"class":281},[61,592,503],{"class":228},[61,594,538],{"class":281},[61,596,513],{"class":228},[61,598,500],{"class":281},[61,600,503],{"class":228},[61,602,166],{"class":281},[61,604,503],{"class":228},[61,606,510],{"class":281},[61,608,609],{"class":228},"))]:\n",[61,611,613,616,619,622,625,628,631,634,637,641,643,646],{"class":63,"line":612},21,[61,614,615],{"class":281},"    print",[61,617,618],{"class":228},"(a, ",[61,620,621],{"class":71},"\"→\"",[61,623,624],{"class":228},", b, ",[61,626,627],{"class":71},"\"US:\"",[61,629,630],{"class":228},", days360(a, b), ",[61,632,633],{"class":71},"\"EU:\"",[61,635,636],{"class":228},", days360(a, b, ",[61,638,640],{"class":639},"sa561","european",[61,642,303],{"class":224},[61,644,645],{"class":281},"True",[61,647,648],{"class":228},"))\n",[10,650,651,652,655],{},"The European method simply caps both days at 30. The US method treats a start date on the ",[23,653,654],{},"last day of any month"," — including 28 February in a common year or 29 February in a leap year — as the 30th, and turns an end date of the 31st into the 30th only if the start has become 30 or more. That is why 28 February 2025 to 31 March 2025 counts 30 days in the US method but 32 in the European one, and why 28 February 2024 (not a month end, because 2024 is a leap year) to 31 March 2024 counts 33. These February cases are exactly the ones to test.",[657,658,666,667,666,671,666,675,666,682,666,692,666,703,666,710,666,716,666,721,666,725,666,729,666,734,666,740,666,745,666,749,666,752,666,755],"svg",{"viewBox":659,"role":660,"ariaLabelledBy":661,"xmlns":664,"style":665},"4 0 752 207","img",[662,663],"yf-30360-t","yf-30360-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  ",[668,669,670],"title",{"id":662},"US and European 30\u002F360 adjustments",[672,673,674],"desc",{"id":663},"The European method caps both start and end day at 30. The US method sets a start date that is the last day of any month, including February, to 30, and changes an end date of 31 to 30 only when the start day is 30 or more. The two differ mainly around February and the 31st.",[676,677],"rect",{"x":179,"y":678,"width":679,"height":680,"fill":681},"0","752","207","#ffffff",[676,683],{"x":684,"y":685,"width":686,"height":687,"rx":688,"fill":689,"stroke":690,"style":691},"20.0","16.0","351.0","175.0","14","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[693,694,696],"g",{"transform":695},"translate(182.5,29.0) scale(1.083)",[697,698],"path",{"d":699,"fill":700,"stroke":701,"style":702},"M4 5h16v15H4z M4 10h16 M8 3v4 M16 3v4","none","var(--brand-strong,#4338ca)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[704,705,709],"text",{"x":706,"y":707,"style":708},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","European (DAYS360 TRUE, basis 4)",[63,711],{"x1":712,"y1":713,"x2":714,"y2":713,"stroke":690,"style":715},"36.0","91.0","355.0","stroke-width:1px",[704,717,720],{"x":706,"y":718,"style":719},"111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","start day 31 → 30",[704,722,724],{"x":706,"y":723,"style":719},"137.0","end day 31 → 30",[704,726,728],{"x":706,"y":727,"style":719},"163.0","28 Feb → 31 Mar = 32 days",[676,730],{"x":731,"y":685,"width":686,"height":687,"rx":688,"fill":732,"stroke":733,"style":691},"389.0","#d9f4f1","var(--teal,#0f9488)",[693,735,737],{"transform":736},"translate(551.5,29.0) scale(1.083)",[697,738],{"d":699,"fill":700,"stroke":739,"style":702},"var(--teal-ink,#0b6157)",[704,741,744],{"x":742,"y":707,"style":743},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","US (DAYS360 FALSE)",[63,746],{"x1":747,"y1":713,"x2":748,"y2":713,"stroke":733,"style":715},"405.0","724.0",[704,750,751],{"x":742,"y":718,"style":719},"start on a month end → 30",[704,753,754],{"x":742,"y":723,"style":719},"end 31 → 30 if start ≥ 30",[704,756,757],{"x":742,"y":727,"style":719},"28 Feb → 31 Mar = 30 days",[47,759,761],{"id":760},"step-2-yearfrac-bases-0-2-3-and-4","Step 2 — YEARFRAC bases 0, 2, 3 and 4",[10,763,764,765,768,769,771,772,775],{},"Bases 2, 3 and 4 are straightforward. Basis 0 is a 30\u002F360 count, but ",[23,766,767],{},"not"," identical to ",[14,770,20],{},"'s US method: when both dates are the last day of February, the end becomes 30 too, and an end date of 31 is reduced only when the ",[23,773,774],{},"original"," start day was 30 or 31:",[52,777,779],{"className":215,"code":778,"language":217,"meta":57,"style":57},"def yearfrac_30360_us(start, end):\n    d1, d2 = start.day, end.day\n    feb_end = lambda d: d.month == 2 and is_month_end(d)\n    if feb_end(start) and feb_end(end):\n        d2 = 30\n    orig_d1 = d1\n    if feb_end(start):\n        d1 = 30\n    if d2 == 31 and orig_d1 >= 30:\n        d2 = 30\n    if d1 == 31:\n        d1 = 30\n    return (360 * (end.year - start.year) + 30 * (end.month - start.month) + (d2 - d1)) \u002F 360\n\ndef yearfrac(start, end, basis=0):\n    if start > end:\n        start, end = end, start\n    actual = (end - start).days\n    if basis == 0:\n        return yearfrac_30360_us(start, end)\n    if basis == 1:\n        return actual \u002F year_length_actual(start, end)\n    if basis == 2:\n        return actual \u002F 360\n    if basis == 3:\n        return actual \u002F 365\n    if basis == 4:\n        return days360(start, end, european=True) \u002F 360\n    raise ValueError(\"basis must be 0–4\")\n",[14,780,781,791,799,822,835,843,853,860,868,889,897,909,917,961,965,981,994,1004,1019,1033,1041,1054,1067,1080,1091,1105,1117,1131,1152],{"__ignoreMap":57},[61,782,783,785,788],{"class":63,"line":64},[61,784,256],{"class":224},[61,786,787],{"class":259}," yearfrac_30360_us",[61,789,790],{"class":228},"(start, end):\n",[61,792,793,795,797],{"class":63,"line":232},[61,794,315],{"class":228},[61,796,303],{"class":224},[61,798,320],{"class":228},[61,800,801,804,806,809,812,814,817,819],{"class":63,"line":246},[61,802,803],{"class":228},"    feb_end ",[61,805,303],{"class":224},[61,807,808],{"class":224}," lambda",[61,810,811],{"class":228}," d: d.month ",[61,813,275],{"class":224},[61,815,816],{"class":281}," 2",[61,818,411],{"class":224},[61,820,821],{"class":228}," is_month_end(d)\n",[61,823,824,826,829,832],{"class":63,"line":253},[61,825,326],{"class":224},[61,827,828],{"class":228}," feb_end(start) ",[61,830,831],{"class":224},"and",[61,833,834],{"class":228}," feb_end(end):\n",[61,836,837,839,841],{"class":63,"line":266},[61,838,355],{"class":228},[61,840,303],{"class":224},[61,842,395],{"class":281},[61,844,845,848,850],{"class":63,"line":287},[61,846,847],{"class":228},"    orig_d1 ",[61,849,303],{"class":224},[61,851,852],{"class":228}," d1\n",[61,854,855,857],{"class":63,"line":292},[61,856,326],{"class":224},[61,858,859],{"class":228}," feb_end(start):\n",[61,861,862,864,866],{"class":63,"line":312},[61,863,335],{"class":228},[61,865,303],{"class":224},[61,867,395],{"class":281},[61,869,870,872,874,876,878,880,883,885,887],{"class":63,"line":323},[61,871,326],{"class":224},[61,873,403],{"class":228},[61,875,275],{"class":224},[61,877,408],{"class":281},[61,879,411],{"class":224},[61,881,882],{"class":228}," orig_d1 ",[61,884,417],{"class":224},[61,886,420],{"class":281},[61,888,375],{"class":228},[61,890,891,893,895],{"class":63,"line":332},[61,892,355],{"class":228},[61,894,303],{"class":224},[61,896,395],{"class":281},[61,898,899,901,903,905,907],{"class":63,"line":352},[61,900,326],{"class":224},[61,902,414],{"class":228},[61,904,275],{"class":224},[61,906,408],{"class":281},[61,908,375],{"class":228},[61,910,911,913,915],{"class":63,"line":369},[61,912,335],{"class":228},[61,914,303],{"class":224},[61,916,395],{"class":281},[61,918,919,921,924,926,928,930,932,934,936,938,940,942,944,946,948,950,952,955,958],{"class":63,"line":378},[61,920,269],{"class":224},[61,922,923],{"class":228}," (",[61,925,135],{"class":281},[61,927,443],{"class":224},[61,929,446],{"class":228},[61,931,449],{"class":224},[61,933,452],{"class":228},[61,935,455],{"class":224},[61,937,420],{"class":281},[61,939,443],{"class":224},[61,941,462],{"class":228},[61,943,449],{"class":224},[61,945,467],{"class":228},[61,947,455],{"class":224},[61,949,472],{"class":228},[61,951,449],{"class":224},[61,953,954],{"class":228}," d1)) ",[61,956,957],{"class":224},"\u002F",[61,959,960],{"class":281}," 360\n",[61,962,963],{"class":63,"line":387},[61,964,250],{"emptyLinePlaceholder":249},[61,966,967,969,972,975,977,979],{"class":63,"line":398},[61,968,256],{"class":224},[61,970,971],{"class":259}," yearfrac",[61,973,974],{"class":228},"(start, end, basis",[61,976,303],{"class":224},[61,978,678],{"class":281},[61,980,309],{"class":228},[61,982,983,985,988,991],{"class":63,"line":425},[61,984,326],{"class":224},[61,986,987],{"class":228}," start ",[61,989,990],{"class":224},">",[61,992,993],{"class":228}," end:\n",[61,995,996,999,1001],{"class":63,"line":435},[61,997,998],{"class":228},"        start, end ",[61,1000,303],{"class":224},[61,1002,1003],{"class":228}," end, start\n",[61,1005,1006,1009,1011,1014,1016],{"class":63,"line":480},[61,1007,1008],{"class":228},"    actual ",[61,1010,303],{"class":224},[61,1012,1013],{"class":228}," (end ",[61,1015,449],{"class":224},[61,1017,1018],{"class":228}," start).days\n",[61,1020,1021,1023,1026,1028,1031],{"class":63,"line":485},[61,1022,326],{"class":224},[61,1024,1025],{"class":228}," basis ",[61,1027,275],{"class":224},[61,1029,1030],{"class":281}," 0",[61,1032,375],{"class":228},[61,1034,1035,1038],{"class":63,"line":556},[61,1036,1037],{"class":224},"        return",[61,1039,1040],{"class":228}," yearfrac_30360_us(start, end)\n",[61,1042,1043,1045,1047,1049,1052],{"class":63,"line":612},[61,1044,326],{"class":224},[61,1046,1025],{"class":228},[61,1048,275],{"class":224},[61,1050,1051],{"class":281}," 1",[61,1053,375],{"class":228},[61,1055,1057,1059,1062,1064],{"class":63,"line":1056},22,[61,1058,1037],{"class":224},[61,1060,1061],{"class":228}," actual ",[61,1063,957],{"class":224},[61,1065,1066],{"class":228}," year_length_actual(start, end)\n",[61,1068,1070,1072,1074,1076,1078],{"class":63,"line":1069},23,[61,1071,326],{"class":224},[61,1073,1025],{"class":228},[61,1075,275],{"class":224},[61,1077,816],{"class":281},[61,1079,375],{"class":228},[61,1081,1083,1085,1087,1089],{"class":63,"line":1082},24,[61,1084,1037],{"class":224},[61,1086,1061],{"class":228},[61,1088,957],{"class":224},[61,1090,960],{"class":281},[61,1092,1094,1096,1098,1100,1103],{"class":63,"line":1093},25,[61,1095,326],{"class":224},[61,1097,1025],{"class":228},[61,1099,275],{"class":224},[61,1101,1102],{"class":281}," 3",[61,1104,375],{"class":228},[61,1106,1108,1110,1112,1114],{"class":63,"line":1107},26,[61,1109,1037],{"class":224},[61,1111,1061],{"class":228},[61,1113,957],{"class":224},[61,1115,1116],{"class":281}," 365\n",[61,1118,1120,1122,1124,1126,1129],{"class":63,"line":1119},27,[61,1121,326],{"class":224},[61,1123,1025],{"class":228},[61,1125,275],{"class":224},[61,1127,1128],{"class":281}," 4",[61,1130,375],{"class":228},[61,1132,1134,1136,1139,1141,1143,1145,1148,1150],{"class":63,"line":1133},28,[61,1135,1037],{"class":224},[61,1137,1138],{"class":228}," days360(start, end, ",[61,1140,640],{"class":639},[61,1142,303],{"class":224},[61,1144,645],{"class":281},[61,1146,1147],{"class":228},") ",[61,1149,957],{"class":224},[61,1151,960],{"class":281},[61,1153,1155,1158,1161,1164,1167],{"class":63,"line":1154},29,[61,1156,1157],{"class":224},"    raise",[61,1159,1160],{"class":281}," ValueError",[61,1162,1163],{"class":228},"(",[61,1165,1166],{"class":71},"\"basis must be 0–4\"",[61,1168,349],{"class":228},[10,1170,1171,1172,1175,1176,1179,1180,1183],{},"The difference between basis 0 and ",[14,1173,1174],{},"DAYS360 \u002F 360"," is a known quirk: from 29 February 2024 to 28 February 2025, ",[14,1177,1178],{},"YEARFRAC(...,0)"," gives exactly 1, because both dates are February month ends, while ",[14,1181,1182],{},"DAYS360(...)\u002F360"," gives 358\u002F360. If a workbook mixes the two, reproduce each separately rather than deriving one from the other. Like Excel, the function swaps the dates if they are in reverse order, so the result is never negative.",[47,1185,1187],{"id":1186},"step-3-basis-1-actualactual","Step 3 — Basis 1, actual\u002Factual",[10,1189,1190],{},"Basis 1 divides actual days by a year length that depends on the dates. Excel's rule: if the period is at most one year, the year has 366 days when it includes a 29 February (or lies within a leap year), otherwise 365; if the period is longer, the year length is the average over every calendar year it touches:",[52,1192,1194],{"className":215,"code":1193,"language":217,"meta":57,"style":57},"def year_length_actual(start, end):\n    one_year_later = start.replace(year=start.year + 1) if not (start.month == 2 and start.day == 29) \\\n        else date(start.year + 1, 3, 1)\n    if end \u003C= one_year_later:\n        if start.year == end.year:\n            return 366 if calendar.isleap(start.year) else 365\n        leap_days = [date(y, 2, 29) for y in range(start.year, end.year + 1) if calendar.isleap(y)]\n        return 366 if any(start \u003C= d \u003C= end for d in leap_days) else 365\n    years = range(start.year, end.year + 1)\n    return sum(366 if calendar.isleap(y) else 365 for y in years) \u002F len(years)\n\nprint(round(yearfrac(date(2024, 2, 29), date(2025, 2, 28), 1), 6))\nprint(round(yearfrac(date(2024, 1, 15), date(2026, 9, 30), 1), 6))\n",[14,1195,1196,1205,1255,1277,1290,1302,1321,1363,1399,1416,1456,1460,1507],{"__ignoreMap":57},[61,1197,1198,1200,1203],{"class":63,"line":64},[61,1199,256],{"class":224},[61,1201,1202],{"class":259}," year_length_actual",[61,1204,790],{"class":228},[61,1206,1207,1210,1212,1215,1218,1220,1223,1225,1227,1229,1232,1235,1238,1240,1242,1244,1247,1249,1252],{"class":63,"line":232},[61,1208,1209],{"class":228},"    one_year_later ",[61,1211,303],{"class":224},[61,1213,1214],{"class":228}," start.replace(",[61,1216,1217],{"class":639},"year",[61,1219,303],{"class":224},[61,1221,1222],{"class":228},"start.year ",[61,1224,455],{"class":224},[61,1226,1051],{"class":281},[61,1228,1147],{"class":228},[61,1230,1231],{"class":224},"if",[61,1233,1234],{"class":224}," not",[61,1236,1237],{"class":228}," (start.month ",[61,1239,275],{"class":224},[61,1241,816],{"class":281},[61,1243,411],{"class":224},[61,1245,1246],{"class":228}," start.day ",[61,1248,275],{"class":224},[61,1250,1251],{"class":281}," 29",[61,1253,1254],{"class":228},") \\\n",[61,1256,1257,1260,1263,1265,1267,1269,1271,1273,1275],{"class":63,"line":246},[61,1258,1259],{"class":224},"        else",[61,1261,1262],{"class":228}," date(start.year ",[61,1264,455],{"class":224},[61,1266,1051],{"class":281},[61,1268,503],{"class":228},[61,1270,166],{"class":281},[61,1272,503],{"class":228},[61,1274,140],{"class":281},[61,1276,349],{"class":228},[61,1278,1279,1281,1284,1287],{"class":63,"line":253},[61,1280,326],{"class":224},[61,1282,1283],{"class":228}," end ",[61,1285,1286],{"class":224},"\u003C=",[61,1288,1289],{"class":228}," one_year_later:\n",[61,1291,1292,1294,1297,1299],{"class":63,"line":266},[61,1293,381],{"class":224},[61,1295,1296],{"class":228}," start.year ",[61,1298,275],{"class":224},[61,1300,1301],{"class":228}," end.year:\n",[61,1303,1304,1307,1310,1313,1316,1319],{"class":63,"line":287},[61,1305,1306],{"class":224},"            return",[61,1308,1309],{"class":281}," 366",[61,1311,1312],{"class":224}," if",[61,1314,1315],{"class":228}," calendar.isleap(start.year) ",[61,1317,1318],{"class":224},"else",[61,1320,1116],{"class":281},[61,1322,1323,1326,1328,1331,1333,1335,1337,1339,1341,1344,1346,1349,1352,1354,1356,1358,1360],{"class":63,"line":292},[61,1324,1325],{"class":228},"        leap_days ",[61,1327,303],{"class":224},[61,1329,1330],{"class":228}," [date(y, ",[61,1332,154],{"class":281},[61,1334,503],{"class":228},[61,1336,570],{"class":281},[61,1338,1147],{"class":228},[61,1340,488],{"class":224},[61,1342,1343],{"class":228}," y ",[61,1345,494],{"class":224},[61,1347,1348],{"class":281}," range",[61,1350,1351],{"class":228},"(start.year, end.year ",[61,1353,455],{"class":224},[61,1355,1051],{"class":281},[61,1357,1147],{"class":228},[61,1359,1231],{"class":224},[61,1361,1362],{"class":228}," calendar.isleap(y)]\n",[61,1364,1365,1367,1369,1371,1374,1377,1379,1382,1384,1386,1388,1390,1392,1395,1397],{"class":63,"line":312},[61,1366,1037],{"class":224},[61,1368,1309],{"class":281},[61,1370,1312],{"class":224},[61,1372,1373],{"class":281}," any",[61,1375,1376],{"class":228},"(start ",[61,1378,1286],{"class":224},[61,1380,1381],{"class":228}," d ",[61,1383,1286],{"class":224},[61,1385,1283],{"class":228},[61,1387,488],{"class":224},[61,1389,1381],{"class":228},[61,1391,494],{"class":224},[61,1393,1394],{"class":228}," leap_days) ",[61,1396,1318],{"class":224},[61,1398,1116],{"class":281},[61,1400,1401,1404,1406,1408,1410,1412,1414],{"class":63,"line":323},[61,1402,1403],{"class":228},"    years ",[61,1405,303],{"class":224},[61,1407,1348],{"class":281},[61,1409,1351],{"class":228},[61,1411,455],{"class":224},[61,1413,1051],{"class":281},[61,1415,349],{"class":228},[61,1417,1418,1420,1423,1425,1428,1430,1433,1435,1438,1441,1443,1445,1448,1450,1453],{"class":63,"line":332},[61,1419,269],{"class":224},[61,1421,1422],{"class":281}," sum",[61,1424,1163],{"class":228},[61,1426,1427],{"class":281},"366",[61,1429,1312],{"class":224},[61,1431,1432],{"class":228}," calendar.isleap(y) ",[61,1434,1318],{"class":224},[61,1436,1437],{"class":281}," 365",[61,1439,1440],{"class":224}," for",[61,1442,1343],{"class":228},[61,1444,494],{"class":224},[61,1446,1447],{"class":228}," years) ",[61,1449,957],{"class":224},[61,1451,1452],{"class":281}," len",[61,1454,1455],{"class":228},"(years)\n",[61,1457,1458],{"class":63,"line":352},[61,1459,250],{"emptyLinePlaceholder":249},[61,1461,1462,1465,1467,1470,1473,1475,1477,1479,1481,1483,1485,1487,1489,1491,1493,1495,1498,1500,1502,1505],{"class":63,"line":369},[61,1463,1464],{"class":281},"print",[61,1466,1163],{"class":228},[61,1468,1469],{"class":281},"round",[61,1471,1472],{"class":228},"(yearfrac(date(",[61,1474,500],{"class":281},[61,1476,503],{"class":228},[61,1478,154],{"class":281},[61,1480,503],{"class":228},[61,1482,570],{"class":281},[61,1484,513],{"class":228},[61,1486,529],{"class":281},[61,1488,503],{"class":228},[61,1490,154],{"class":281},[61,1492,503],{"class":228},[61,1494,538],{"class":281},[61,1496,1497],{"class":228},"), ",[61,1499,140],{"class":281},[61,1501,1497],{"class":228},[61,1503,1504],{"class":281},"6",[61,1506,648],{"class":228},[61,1508,1509,1511,1513,1515,1517,1519,1521,1523,1525,1528,1530,1533,1535,1538,1540,1542,1544,1546,1548,1550],{"class":63,"line":378},[61,1510,1464],{"class":281},[61,1512,1163],{"class":228},[61,1514,1469],{"class":281},[61,1516,1472],{"class":228},[61,1518,500],{"class":281},[61,1520,503],{"class":228},[61,1522,140],{"class":281},[61,1524,503],{"class":228},[61,1526,1527],{"class":281},"15",[61,1529,513],{"class":228},[61,1531,1532],{"class":281},"2026",[61,1534,503],{"class":228},[61,1536,1537],{"class":281},"9",[61,1539,503],{"class":228},[61,1541,346],{"class":281},[61,1543,1497],{"class":228},[61,1545,140],{"class":281},[61,1547,1497],{"class":228},[61,1549,1504],{"class":281},[61,1551,648],{"class":228},[10,1553,1554,1555,1557],{},"From 29 February 2024 to 28 February 2025 is 365 days within a period that includes a 29 February, so the year counts as 366: 0.997268. From 15 January 2024 to 30 September 2026 is more than a year, so the 989 days are divided by the average length of 2024, 2025 and 2026 — (366 + 365 + 365) \u002F 3 — giving 2.707117. This basis is ",[23,1556,767],{}," the ISDA actual\u002Factual convention used in many bond markets, which splits the period by calendar year; if a contract specifies ISDA, implement that instead of copying Excel.",[657,1559,666,1564,666,1567,666,1570,666,1573,666,1579,666,1585,666,1590,666,1594,666,1598,666,1601,666,1606,666,1615,666,1620,666,1625,666,1631,666,1636,666,1641,666,1643,666,1646,666,1650,666,1653,666,1656,666,1661,666,1664,666,1667,666,1669,666,1672,666,1676,666,1679,666,1684,666,1691,666,1695],{"viewBox":1560,"role":660,"ariaLabelledBy":1561,"xmlns":664,"style":665},"4 0 752 272",[1562,1563],"yf-basis1-t","yf-basis1-d",[668,1565,1566],{"id":1562},"How basis 1 chooses the year length",[672,1568,1569],{"id":1563},"If the period is at most one year, the year length is 366 when the dates lie in one leap year or the period contains 29 February, and 365 otherwise. If the period is longer than a year, the year length is the average length of all calendar years from the start year to the end year.",[676,1571],{"x":179,"y":678,"width":679,"height":1572,"fill":681},"272",[676,1574],{"x":1575,"y":685,"width":1576,"height":1577,"rx":1578,"fill":689,"stroke":690,"style":691},"240.0","280.0","75.5","12",[693,1580,1582],{"transform":1581},"translate(368.0,29.0) scale(1.000)",[697,1583],{"d":699,"fill":700,"stroke":701,"style":1584},"stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[704,1586,1589],{"x":1587,"y":1577,"style":1588},"380.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Is the period at most one year?",[63,1591],{"x1":1587,"y1":1592,"x2":1587,"y2":1593,"stroke":690,"style":691},"91.5","111.5",[63,1595],{"x1":1596,"y1":1593,"x2":1597,"y2":1593,"stroke":690,"style":691},"134.7","625.3",[63,1599],{"x1":1596,"y1":1593,"x2":1596,"y2":1600,"stroke":690,"style":691},"157.5",[1602,1603],"polygon",{"points":1604,"fill":1605},"134.7,161.5 129.7,152.5 139.7,152.5","#5b5cf0",[676,1607],{"x":1608,"y":1609,"width":1610,"height":1611,"rx":1612,"fill":681,"stroke":1613,"style":1614},"64.6","121.5","140.2","21.0","10","var(--line,#cdd5e6)","stroke-width:1.2px",[704,1616,1619],{"x":1596,"y":1617,"style":1618},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","yes, contains 29 Feb",[676,1621],{"x":684,"y":1622,"width":1623,"height":1624,"rx":1578,"fill":732,"stroke":733,"style":691},"163.5","229.3","93.0",[693,1626,1628],{"transform":1627},"translate(122.7,176.5) scale(1.000)",[697,1629],{"d":1630,"fill":700,"stroke":739,"style":1584},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16",[704,1632,1635],{"x":1596,"y":1633,"style":1634},"223.0","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","divide by 366",[704,1637,1640],{"x":1596,"y":1638,"style":1639},"242.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","e.g. 29 Feb 24 → 28 Feb 25",[63,1642],{"x1":1587,"y1":1593,"x2":1587,"y2":1600,"stroke":690,"style":691},[1602,1644],{"points":1645,"fill":1605},"380.0,161.5 375.0,152.5 385.0,152.5",[676,1647],{"x":1648,"y":1609,"width":1649,"height":1611,"rx":1612,"fill":681,"stroke":1613,"style":1614},"327.9","104.1",[704,1651,1652],{"x":1587,"y":1617,"style":1618},"yes, no 29 Feb",[676,1654],{"x":1655,"y":1622,"width":1623,"height":1624,"rx":1578,"fill":689,"stroke":690,"style":691},"265.3",[693,1657,1659],{"transform":1658},"translate(368.0,176.5) scale(1.000)",[697,1660],{"d":1630,"fill":700,"stroke":701,"style":1584},[704,1662,1663],{"x":1587,"y":1633,"style":1588},"divide by 365",[704,1665,1666],{"x":1587,"y":1638,"style":1639},"e.g. Jul 24 → Jun 25",[63,1668],{"x1":1597,"y1":1593,"x2":1597,"y2":1600,"stroke":690,"style":691},[1602,1670],{"points":1671,"fill":1605},"625.3,161.5 620.3,152.5 630.3,152.5",[676,1673],{"x":1674,"y":1609,"width":1675,"height":1611,"rx":1612,"fill":681,"stroke":1613,"style":1614},"608.9","32.9",[704,1677,1678],{"x":1597,"y":1617,"style":1618},"no",[676,1680],{"x":1681,"y":1622,"width":1623,"height":1624,"rx":1578,"fill":1682,"stroke":1683,"style":691},"510.7","#fdefd8","var(--gold,#b4740a)",[693,1685,1687],{"transform":1686},"translate(613.3,176.5) scale(1.000)",[697,1688],{"d":1689,"fill":700,"stroke":1690,"style":1584},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5","var(--gold-ink,#7a4e06)",[704,1692,1694],{"x":1597,"y":1633,"style":1693},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","average year length",[704,1696,1697],{"x":1597,"y":1638,"style":1639},"(366+365+365)\u002F3",[47,1699,1701],{"id":1700},"step-4-check-against-a-spreadsheets-own-results","Step 4 — Check against a spreadsheet's own results",[10,1703,1704],{},"Day-count code is only trustworthy once it agrees with the spreadsheet on awkward dates. Write the formulas into a workbook, recalculate, and compare every basis:",[52,1706,1708],{"className":215,"code":1707,"language":217,"meta":57,"style":57},"import subprocess\nfrom openpyxl import Workbook, load_workbook\n\npairs = [(date(2024, 1, 31), date(2024, 3, 31)), (date(2024, 2, 29), date(2025, 2, 28)), (date(2025, 2, 28), date(2025, 3, 31)),\n         (date(2023, 12, 31), date(2024, 12, 31)), (date(2024, 1, 15), date(2026, 9, 30)), (date(2025, 1, 30), date(2025, 2, 28)),\n         (date(2024, 2, 28), date(2024, 3, 31)), (date(2024, 7, 1), date(2025, 6, 30))]\nwb = Workbook()\nws = wb.active\nfor i, (a, b) in enumerate(pairs, start=1):\n    ws.cell(i, 1, a); ws.cell(i, 2, b)\n    for basis in range(5):\n        ws.cell(i, 3 + basis, f\"=YEARFRAC(A{i},B{i},{basis})\")\n    ws.cell(i, 8, f\"=DAYS360(A{i},B{i},FALSE)\")\n    ws.cell(i, 9, f\"=DAYS360(A{i},B{i},TRUE)\")\nwb.save(\"daycount.xlsx\")\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"xlsx\", \"--outdir\", \"calc\", \"daycount.xlsx\"], check=True, capture_output=True)\nsheet = load_workbook(\"calc\u002Fdaycount.xlsx\", data_only=True).active\n\nmismatches = []\nfor i, (a, b) in enumerate(pairs, start=1):\n    for basis in range(5):\n        if abs(sheet.cell(i, 3 + basis).value - yearfrac(a, b, basis)) > 1e-9:\n            mismatches.append((a, b, f\"basis {basis}\"))\n    if sheet.cell(i, 8).value != days360(a, b) or sheet.cell(i, 9).value != days360(a, b, True):\n        mismatches.append((a, b, \"DAYS360\"))\nprint(f\"{len(pairs) * 7} comparisons, mismatches: {mismatches or 'none'}\")\n",[14,1709,1710,1717,1729,1733,1814,1892,1946,1956,1966,1990,2005,2023,2076,2109,2140,2150,2208,2233,2237,2247,2267,2283,2312,2333,2369,2379],{"__ignoreMap":57},[61,1711,1712,1714],{"class":63,"line":64},[61,1713,225],{"class":224},[61,1715,1716],{"class":228}," subprocess\n",[61,1718,1719,1721,1724,1726],{"class":63,"line":232},[61,1720,235],{"class":224},[61,1722,1723],{"class":228}," openpyxl ",[61,1725,225],{"class":224},[61,1727,1728],{"class":228}," Workbook, load_workbook\n",[61,1730,1731],{"class":63,"line":246},[61,1732,250],{"emptyLinePlaceholder":249},[61,1734,1735,1738,1740,1742,1744,1746,1748,1750,1752,1754,1756,1758,1760,1762,1764,1766,1768,1770,1772,1774,1776,1778,1780,1782,1784,1786,1788,1790,1792,1794,1796,1798,1800,1802,1804,1806,1808,1810,1812],{"class":63,"line":253},[61,1736,1737],{"class":228},"pairs ",[61,1739,303],{"class":224},[61,1741,497],{"class":228},[61,1743,500],{"class":281},[61,1745,503],{"class":228},[61,1747,140],{"class":281},[61,1749,503],{"class":228},[61,1751,510],{"class":281},[61,1753,513],{"class":228},[61,1755,500],{"class":281},[61,1757,503],{"class":228},[61,1759,166],{"class":281},[61,1761,503],{"class":228},[61,1763,510],{"class":281},[61,1765,526],{"class":228},[61,1767,500],{"class":281},[61,1769,503],{"class":228},[61,1771,154],{"class":281},[61,1773,503],{"class":228},[61,1775,570],{"class":281},[61,1777,513],{"class":228},[61,1779,529],{"class":281},[61,1781,503],{"class":228},[61,1783,154],{"class":281},[61,1785,503],{"class":228},[61,1787,538],{"class":281},[61,1789,526],{"class":228},[61,1791,529],{"class":281},[61,1793,503],{"class":228},[61,1795,154],{"class":281},[61,1797,503],{"class":228},[61,1799,538],{"class":281},[61,1801,513],{"class":228},[61,1803,529],{"class":281},[61,1805,503],{"class":228},[61,1807,166],{"class":281},[61,1809,503],{"class":228},[61,1811,510],{"class":281},[61,1813,553],{"class":228},[61,1815,1816,1819,1822,1824,1826,1828,1830,1832,1834,1836,1838,1840,1842,1844,1846,1848,1850,1852,1854,1856,1858,1860,1862,1864,1866,1868,1870,1872,1874,1876,1878,1880,1882,1884,1886,1888,1890],{"class":63,"line":266},[61,1817,1818],{"class":228},"         (date(",[61,1820,1821],{"class":281},"2023",[61,1823,503],{"class":228},[61,1825,1578],{"class":281},[61,1827,503],{"class":228},[61,1829,510],{"class":281},[61,1831,513],{"class":228},[61,1833,500],{"class":281},[61,1835,503],{"class":228},[61,1837,1578],{"class":281},[61,1839,503],{"class":228},[61,1841,510],{"class":281},[61,1843,526],{"class":228},[61,1845,500],{"class":281},[61,1847,503],{"class":228},[61,1849,140],{"class":281},[61,1851,503],{"class":228},[61,1853,1527],{"class":281},[61,1855,513],{"class":228},[61,1857,1532],{"class":281},[61,1859,503],{"class":228},[61,1861,1537],{"class":281},[61,1863,503],{"class":228},[61,1865,346],{"class":281},[61,1867,526],{"class":228},[61,1869,529],{"class":281},[61,1871,503],{"class":228},[61,1873,140],{"class":281},[61,1875,503],{"class":228},[61,1877,346],{"class":281},[61,1879,513],{"class":228},[61,1881,529],{"class":281},[61,1883,503],{"class":228},[61,1885,154],{"class":281},[61,1887,503],{"class":228},[61,1889,538],{"class":281},[61,1891,553],{"class":228},[61,1893,1894,1896,1898,1900,1902,1904,1906,1908,1910,1912,1914,1916,1918,1920,1922,1924,1927,1929,1931,1933,1935,1937,1939,1941,1943],{"class":63,"line":287},[61,1895,1818],{"class":228},[61,1897,500],{"class":281},[61,1899,503],{"class":228},[61,1901,154],{"class":281},[61,1903,503],{"class":228},[61,1905,538],{"class":281},[61,1907,513],{"class":228},[61,1909,500],{"class":281},[61,1911,503],{"class":228},[61,1913,166],{"class":281},[61,1915,503],{"class":228},[61,1917,510],{"class":281},[61,1919,526],{"class":228},[61,1921,500],{"class":281},[61,1923,503],{"class":228},[61,1925,1926],{"class":281},"7",[61,1928,503],{"class":228},[61,1930,140],{"class":281},[61,1932,513],{"class":228},[61,1934,529],{"class":281},[61,1936,503],{"class":228},[61,1938,1504],{"class":281},[61,1940,503],{"class":228},[61,1942,346],{"class":281},[61,1944,1945],{"class":228},"))]\n",[61,1947,1948,1951,1953],{"class":63,"line":292},[61,1949,1950],{"class":228},"wb ",[61,1952,303],{"class":224},[61,1954,1955],{"class":228}," Workbook()\n",[61,1957,1958,1961,1963],{"class":63,"line":312},[61,1959,1960],{"class":228},"ws ",[61,1962,303],{"class":224},[61,1964,1965],{"class":228}," wb.active\n",[61,1967,1968,1970,1973,1975,1978,1981,1984,1986,1988],{"class":63,"line":323},[61,1969,488],{"class":224},[61,1971,1972],{"class":228}," i, (a, b) ",[61,1974,494],{"class":224},[61,1976,1977],{"class":281}," enumerate",[61,1979,1980],{"class":228},"(pairs, ",[61,1982,1983],{"class":639},"start",[61,1985,303],{"class":224},[61,1987,140],{"class":281},[61,1989,309],{"class":228},[61,1991,1992,1995,1997,2000,2002],{"class":63,"line":332},[61,1993,1994],{"class":228},"    ws.cell(i, ",[61,1996,140],{"class":281},[61,1998,1999],{"class":228},", a); ws.cell(i, ",[61,2001,154],{"class":281},[61,2003,2004],{"class":228},", b)\n",[61,2006,2007,2010,2012,2014,2016,2018,2021],{"class":63,"line":352},[61,2008,2009],{"class":224},"    for",[61,2011,1025],{"class":228},[61,2013,494],{"class":224},[61,2015,1348],{"class":281},[61,2017,1163],{"class":228},[61,2019,2020],{"class":281},"5",[61,2022,309],{"class":228},[61,2024,2025,2028,2030,2033,2036,2039,2042,2046,2049,2052,2055,2057,2059,2061,2064,2066,2069,2071,2074],{"class":63,"line":369},[61,2026,2027],{"class":228},"        ws.cell(i, ",[61,2029,166],{"class":281},[61,2031,2032],{"class":224}," +",[61,2034,2035],{"class":228}," basis, ",[61,2037,2038],{"class":224},"f",[61,2040,2041],{"class":71},"\"=YEARFRAC(A",[61,2043,2045],{"class":2044},"sSjpA","{",[61,2047,2048],{"class":228},"i",[61,2050,2051],{"class":2044},"}",[61,2053,2054],{"class":71},",B",[61,2056,2045],{"class":2044},[61,2058,2048],{"class":228},[61,2060,2051],{"class":2044},[61,2062,2063],{"class":71},",",[61,2065,2045],{"class":2044},[61,2067,2068],{"class":228},"basis",[61,2070,2051],{"class":2044},[61,2072,2073],{"class":71},")\"",[61,2075,349],{"class":228},[61,2077,2078,2080,2083,2085,2087,2090,2092,2094,2096,2098,2100,2102,2104,2107],{"class":63,"line":378},[61,2079,1994],{"class":228},[61,2081,2082],{"class":281},"8",[61,2084,503],{"class":228},[61,2086,2038],{"class":224},[61,2088,2089],{"class":71},"\"=DAYS360(A",[61,2091,2045],{"class":2044},[61,2093,2048],{"class":228},[61,2095,2051],{"class":2044},[61,2097,2054],{"class":71},[61,2099,2045],{"class":2044},[61,2101,2048],{"class":228},[61,2103,2051],{"class":2044},[61,2105,2106],{"class":71},",FALSE)\"",[61,2108,349],{"class":228},[61,2110,2111,2113,2115,2117,2119,2121,2123,2125,2127,2129,2131,2133,2135,2138],{"class":63,"line":387},[61,2112,1994],{"class":228},[61,2114,1537],{"class":281},[61,2116,503],{"class":228},[61,2118,2038],{"class":224},[61,2120,2089],{"class":71},[61,2122,2045],{"class":2044},[61,2124,2048],{"class":228},[61,2126,2051],{"class":2044},[61,2128,2054],{"class":71},[61,2130,2045],{"class":2044},[61,2132,2048],{"class":228},[61,2134,2051],{"class":2044},[61,2136,2137],{"class":71},",TRUE)\"",[61,2139,349],{"class":228},[61,2141,2142,2145,2148],{"class":63,"line":398},[61,2143,2144],{"class":228},"wb.save(",[61,2146,2147],{"class":71},"\"daycount.xlsx\"",[61,2149,349],{"class":228},[61,2151,2152,2155,2158,2160,2163,2165,2168,2170,2173,2175,2178,2180,2183,2185,2187,2190,2193,2195,2197,2199,2202,2204,2206],{"class":63,"line":425},[61,2153,2154],{"class":228},"subprocess.run([",[61,2156,2157],{"class":71},"\"soffice\"",[61,2159,503],{"class":228},[61,2161,2162],{"class":71},"\"--headless\"",[61,2164,503],{"class":228},[61,2166,2167],{"class":71},"\"--convert-to\"",[61,2169,503],{"class":228},[61,2171,2172],{"class":71},"\"xlsx\"",[61,2174,503],{"class":228},[61,2176,2177],{"class":71},"\"--outdir\"",[61,2179,503],{"class":228},[61,2181,2182],{"class":71},"\"calc\"",[61,2184,503],{"class":228},[61,2186,2147],{"class":71},[61,2188,2189],{"class":228},"], ",[61,2191,2192],{"class":639},"check",[61,2194,303],{"class":224},[61,2196,645],{"class":281},[61,2198,503],{"class":228},[61,2200,2201],{"class":639},"capture_output",[61,2203,303],{"class":224},[61,2205,645],{"class":281},[61,2207,349],{"class":228},[61,2209,2210,2213,2215,2218,2221,2223,2226,2228,2230],{"class":63,"line":435},[61,2211,2212],{"class":228},"sheet ",[61,2214,303],{"class":224},[61,2216,2217],{"class":228}," load_workbook(",[61,2219,2220],{"class":71},"\"calc\u002Fdaycount.xlsx\"",[61,2222,503],{"class":228},[61,2224,2225],{"class":639},"data_only",[61,2227,303],{"class":224},[61,2229,645],{"class":281},[61,2231,2232],{"class":228},").active\n",[61,2234,2235],{"class":63,"line":480},[61,2236,250],{"emptyLinePlaceholder":249},[61,2238,2239,2242,2244],{"class":63,"line":485},[61,2240,2241],{"class":228},"mismatches ",[61,2243,303],{"class":224},[61,2245,2246],{"class":228}," []\n",[61,2248,2249,2251,2253,2255,2257,2259,2261,2263,2265],{"class":63,"line":556},[61,2250,488],{"class":224},[61,2252,1972],{"class":228},[61,2254,494],{"class":224},[61,2256,1977],{"class":281},[61,2258,1980],{"class":228},[61,2260,1983],{"class":639},[61,2262,303],{"class":224},[61,2264,140],{"class":281},[61,2266,309],{"class":228},[61,2268,2269,2271,2273,2275,2277,2279,2281],{"class":63,"line":612},[61,2270,2009],{"class":224},[61,2272,1025],{"class":228},[61,2274,494],{"class":224},[61,2276,1348],{"class":281},[61,2278,1163],{"class":228},[61,2280,2020],{"class":281},[61,2282,309],{"class":228},[61,2284,2285,2287,2290,2293,2295,2297,2300,2302,2305,2307,2310],{"class":63,"line":1056},[61,2286,381],{"class":224},[61,2288,2289],{"class":281}," abs",[61,2291,2292],{"class":228},"(sheet.cell(i, ",[61,2294,166],{"class":281},[61,2296,2032],{"class":224},[61,2298,2299],{"class":228}," basis).value ",[61,2301,449],{"class":224},[61,2303,2304],{"class":228}," yearfrac(a, b, basis)) ",[61,2306,990],{"class":224},[61,2308,2309],{"class":281}," 1e-9",[61,2311,375],{"class":228},[61,2313,2314,2317,2319,2322,2324,2326,2328,2331],{"class":63,"line":1069},[61,2315,2316],{"class":228},"            mismatches.append((a, b, ",[61,2318,2038],{"class":224},[61,2320,2321],{"class":71},"\"basis ",[61,2323,2045],{"class":2044},[61,2325,2068],{"class":228},[61,2327,2051],{"class":2044},[61,2329,2330],{"class":71},"\"",[61,2332,648],{"class":228},[61,2334,2335,2337,2340,2342,2345,2348,2351,2354,2356,2358,2360,2362,2365,2367],{"class":63,"line":1082},[61,2336,326],{"class":224},[61,2338,2339],{"class":228}," sheet.cell(i, ",[61,2341,2082],{"class":281},[61,2343,2344],{"class":228},").value ",[61,2346,2347],{"class":224},"!=",[61,2349,2350],{"class":228}," days360(a, b) ",[61,2352,2353],{"class":224},"or",[61,2355,2339],{"class":228},[61,2357,1537],{"class":281},[61,2359,2344],{"class":228},[61,2361,2347],{"class":224},[61,2363,2364],{"class":228}," days360(a, b, ",[61,2366,645],{"class":281},[61,2368,309],{"class":228},[61,2370,2371,2374,2377],{"class":63,"line":1093},[61,2372,2373],{"class":228},"        mismatches.append((a, b, ",[61,2375,2376],{"class":71},"\"DAYS360\"",[61,2378,648],{"class":228},[61,2380,2381,2383,2385,2387,2389,2391,2394,2397,2400,2403,2405,2408,2410,2412,2414,2417,2419,2421],{"class":63,"line":1107},[61,2382,1464],{"class":281},[61,2384,1163],{"class":228},[61,2386,2038],{"class":224},[61,2388,2330],{"class":71},[61,2390,2045],{"class":2044},[61,2392,2393],{"class":281},"len",[61,2395,2396],{"class":228},"(pairs) ",[61,2398,2399],{"class":224},"*",[61,2401,2402],{"class":281}," 7",[61,2404,2051],{"class":2044},[61,2406,2407],{"class":71}," comparisons, mismatches: ",[61,2409,2045],{"class":2044},[61,2411,2241],{"class":228},[61,2413,2353],{"class":224},[61,2415,2416],{"class":71}," 'none'",[61,2418,2051],{"class":2044},[61,2420,2330],{"class":71},[61,2422,349],{"class":228},[10,2424,2425,2426,2428,2429,45],{},"All 56 comparisons — five bases and two ",[14,2427,20],{}," methods for eight date pairs chosen around month ends, February and leap years — agree to within floating-point precision. Keep a test like this next to the functions: if someone \"simplifies\" the February rules later, it fails on exactly the dates that matter. Recalculating workbooks for tests is covered in ",[41,2430,2432],{"href":2431},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Frecalculate-excel-formulas-without-excel-in-python\u002F","Recalculate Excel Formulas Without Excel in Python",[47,2434,2436],{"id":2435},"step-5-accrued-interest-in-pandas","Step 5 — Accrued interest in pandas",[10,2438,2439],{},"With the scalar functions tested, apply them to a table of loans to compute accrued interest to a reporting date:",[52,2441,2443],{"className":215,"code":2442,"language":217,"meta":57,"style":57},"import pandas as pd\n\nloans = pd.DataFrame({\n    \"Loan\": [\"L-01\", \"L-02\", \"L-03\", \"L-04\"],\n    \"Principal\": [250_000, 80_000, 1_200_000, 45_000],\n    \"Rate\": [0.052, 0.061, 0.0475, 0.07],\n    \"Last payment\": pd.to_datetime([\"2026-06-30\", \"2026-08-31\", \"2026-02-28\", \"2026-09-15\"]),\n    \"Basis\": [0, 4, 2, 1],\n})\nas_of = date(2026, 9, 30)\nloans[\"Year fraction\"] = [yearfrac(d.date(), as_of, b) for d, b in zip(loans[\"Last payment\"], loans[\"Basis\"])]\nloans[\"Accrued\"] = (loans[\"Principal\"] * loans[\"Rate\"] * loans[\"Year fraction\"]).round(2)\nprint(loans.round({\"Year fraction\": 6}).to_string(index=False))\n",[14,2444,2445,2458,2462,2472,2501,2528,2555,2584,2607,2612,2634,2675,2717],{"__ignoreMap":57},[61,2446,2447,2449,2452,2455],{"class":63,"line":64},[61,2448,225],{"class":224},[61,2450,2451],{"class":228}," pandas ",[61,2453,2454],{"class":224},"as",[61,2456,2457],{"class":228}," pd\n",[61,2459,2460],{"class":63,"line":232},[61,2461,250],{"emptyLinePlaceholder":249},[61,2463,2464,2467,2469],{"class":63,"line":246},[61,2465,2466],{"class":228},"loans ",[61,2468,303],{"class":224},[61,2470,2471],{"class":228}," pd.DataFrame({\n",[61,2473,2474,2477,2480,2483,2485,2488,2490,2493,2495,2498],{"class":63,"line":253},[61,2475,2476],{"class":71},"    \"Loan\"",[61,2478,2479],{"class":228},": [",[61,2481,2482],{"class":71},"\"L-01\"",[61,2484,503],{"class":228},[61,2486,2487],{"class":71},"\"L-02\"",[61,2489,503],{"class":228},[61,2491,2492],{"class":71},"\"L-03\"",[61,2494,503],{"class":228},[61,2496,2497],{"class":71},"\"L-04\"",[61,2499,2500],{"class":228},"],\n",[61,2502,2503,2506,2508,2511,2513,2516,2518,2521,2523,2526],{"class":63,"line":266},[61,2504,2505],{"class":71},"    \"Principal\"",[61,2507,2479],{"class":228},[61,2509,2510],{"class":281},"250_000",[61,2512,503],{"class":228},[61,2514,2515],{"class":281},"80_000",[61,2517,503],{"class":228},[61,2519,2520],{"class":281},"1_200_000",[61,2522,503],{"class":228},[61,2524,2525],{"class":281},"45_000",[61,2527,2500],{"class":228},[61,2529,2530,2533,2535,2538,2540,2543,2545,2548,2550,2553],{"class":63,"line":287},[61,2531,2532],{"class":71},"    \"Rate\"",[61,2534,2479],{"class":228},[61,2536,2537],{"class":281},"0.052",[61,2539,503],{"class":228},[61,2541,2542],{"class":281},"0.061",[61,2544,503],{"class":228},[61,2546,2547],{"class":281},"0.0475",[61,2549,503],{"class":228},[61,2551,2552],{"class":281},"0.07",[61,2554,2500],{"class":228},[61,2556,2557,2560,2563,2566,2568,2571,2573,2576,2578,2581],{"class":63,"line":292},[61,2558,2559],{"class":71},"    \"Last payment\"",[61,2561,2562],{"class":228},": pd.to_datetime([",[61,2564,2565],{"class":71},"\"2026-06-30\"",[61,2567,503],{"class":228},[61,2569,2570],{"class":71},"\"2026-08-31\"",[61,2572,503],{"class":228},[61,2574,2575],{"class":71},"\"2026-02-28\"",[61,2577,503],{"class":228},[61,2579,2580],{"class":71},"\"2026-09-15\"",[61,2582,2583],{"class":228},"]),\n",[61,2585,2586,2589,2591,2593,2595,2597,2599,2601,2603,2605],{"class":63,"line":312},[61,2587,2588],{"class":71},"    \"Basis\"",[61,2590,2479],{"class":228},[61,2592,678],{"class":281},[61,2594,503],{"class":228},[61,2596,179],{"class":281},[61,2598,503],{"class":228},[61,2600,154],{"class":281},[61,2602,503],{"class":228},[61,2604,140],{"class":281},[61,2606,2500],{"class":228},[61,2608,2609],{"class":63,"line":323},[61,2610,2611],{"class":228},"})\n",[61,2613,2614,2617,2619,2622,2624,2626,2628,2630,2632],{"class":63,"line":332},[61,2615,2616],{"class":228},"as_of ",[61,2618,303],{"class":224},[61,2620,2621],{"class":228}," date(",[61,2623,1532],{"class":281},[61,2625,503],{"class":228},[61,2627,1537],{"class":281},[61,2629,503],{"class":228},[61,2631,346],{"class":281},[61,2633,349],{"class":228},[61,2635,2636,2639,2642,2645,2647,2650,2652,2655,2657,2660,2663,2666,2669,2672],{"class":63,"line":352},[61,2637,2638],{"class":228},"loans[",[61,2640,2641],{"class":71},"\"Year fraction\"",[61,2643,2644],{"class":228},"] ",[61,2646,303],{"class":224},[61,2648,2649],{"class":228}," [yearfrac(d.date(), as_of, b) ",[61,2651,488],{"class":224},[61,2653,2654],{"class":228}," d, b ",[61,2656,494],{"class":224},[61,2658,2659],{"class":281}," zip",[61,2661,2662],{"class":228},"(loans[",[61,2664,2665],{"class":71},"\"Last payment\"",[61,2667,2668],{"class":228},"], loans[",[61,2670,2671],{"class":71},"\"Basis\"",[61,2673,2674],{"class":228},"])]\n",[61,2676,2677,2679,2682,2684,2686,2689,2692,2694,2696,2699,2702,2704,2706,2708,2710,2713,2715],{"class":63,"line":369},[61,2678,2638],{"class":228},[61,2680,2681],{"class":71},"\"Accrued\"",[61,2683,2644],{"class":228},[61,2685,303],{"class":224},[61,2687,2688],{"class":228}," (loans[",[61,2690,2691],{"class":71},"\"Principal\"",[61,2693,2644],{"class":228},[61,2695,2399],{"class":224},[61,2697,2698],{"class":228}," loans[",[61,2700,2701],{"class":71},"\"Rate\"",[61,2703,2644],{"class":228},[61,2705,2399],{"class":224},[61,2707,2698],{"class":228},[61,2709,2641],{"class":71},[61,2711,2712],{"class":228},"]).round(",[61,2714,154],{"class":281},[61,2716,349],{"class":228},[61,2718,2719,2721,2724,2726,2729,2731,2734,2737,2739,2741],{"class":63,"line":378},[61,2720,1464],{"class":281},[61,2722,2723],{"class":228},"(loans.round({",[61,2725,2641],{"class":71},[61,2727,2728],{"class":228},": ",[61,2730,1504],{"class":281},[61,2732,2733],{"class":228},"}).to_string(",[61,2735,2736],{"class":639},"index",[61,2738,303],{"class":224},[61,2740,306],{"class":281},[61,2742,648],{"class":228},[10,2744,2745,2746,2749,2750,45],{},"Each loan uses the basis its contract specifies, stored in a column so the table documents itself. The accrued interest is principal × rate × year fraction, exactly as the Excel formula ",[14,2747,2748],{},"=Principal*Rate*YEARFRAC(LastPayment, AsOf, Basis)"," would compute it. A list comprehension over the scalar function is perfectly fast for thousands of loans; for millions, vectorise the actual\u002F360 and actual\u002F365 bases with plain date arithmetic, which needs no loop. Related financial functions are covered in ",[41,2751,2753],{"href":2752},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fpmt-npv-and-irr-in-python\u002F","PMT, NPV and IRR in Python",[657,2755,666,2761,666,2764,666,2767,666,2772,666,2778,666,2782,666,2788,666,2792,666,2796,666,2800,666,2804,666,2808,666,2812,666,2816,666,2820,666,2823,666,2827,666,2829,666,2832,666,2834,666,2836,666,2838,666,2841,666,2844,666,2847,666,2849,666,2853,666,2855,666,2858,666,2860,666,2863,666,2865,666,2869,666,2872,666,2875,666,2877,666,2880,666,2882,666,2885,666,2887,666,2890,666,2892,666,2895,666,2898,666,2901,666,2903,666,2906,666,2908,666,2911,666,2913,666,2916,666,2918,666,2921,666,2924,666,2927,666,2929,666,2932,666,2934,666,2937,666,2939,666,2942,666,2944],{"viewBox":2756,"role":660,"ariaLabelledBy":2757,"xmlns":664,"style":2760},"177 0 405 196",[2758,2759],"yf-accrual-t","yf-accrual-d","width:100%;max-width:405px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[668,2762,2763],{"id":2758},"Accrued interest with each loan's own basis",[672,2765,2766],{"id":2759},"Four loans with principal, rate, last payment date and day-count basis. The year fraction to 30 September 2026 is computed with the loan's basis, and accrued interest is principal times rate times the year fraction.",[676,2768],{"x":2769,"y":678,"width":2770,"height":2771,"fill":681},"177","405","196",[676,2773],{"x":2774,"y":685,"width":2775,"height":2776,"rx":678,"fill":2777,"stroke":1613,"style":715},"193.7","32.0","24.0","#e7ebef",[676,2779],{"x":2780,"y":685,"width":2781,"height":2776,"rx":678,"fill":2777,"stroke":1613,"style":715},"225.7","50.3",[704,2783,2787],{"x":2784,"y":2785,"style":2786},"250.8","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[676,2789],{"x":2790,"y":685,"width":2791,"height":2776,"rx":678,"fill":2777,"stroke":1613,"style":715},"276.0","97.0",[704,2793,2795],{"x":2794,"y":2785,"style":2786},"324.5","B",[676,2797],{"x":2798,"y":685,"width":2799,"height":2776,"rx":678,"fill":2777,"stroke":1613,"style":715},"373.0","98.8",[704,2801,2803],{"x":2802,"y":2785,"style":2786},"422.4","C",[676,2805],{"x":2806,"y":685,"width":2807,"height":2776,"rx":678,"fill":2777,"stroke":1613,"style":715},"471.7","94.5",[704,2809,2811],{"x":2810,"y":2785,"style":2786},"519.0","D",[676,2813],{"x":2774,"y":2814,"width":2775,"height":2815,"rx":678,"fill":2777,"stroke":1613,"style":715},"40.0","28.0",[704,2817,140],{"x":2818,"y":2819,"style":2786},"209.7","59.0",[676,2821],{"x":2780,"y":2814,"width":2781,"height":2815,"rx":678,"fill":689,"stroke":690,"style":2822},"stroke-width:1.5px",[704,2824,2826],{"x":2784,"y":2819,"style":2825},"font-size:10.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Loan",[676,2828],{"x":2790,"y":2814,"width":2791,"height":2815,"rx":678,"fill":689,"stroke":690,"style":2822},[704,2830,2831],{"x":2794,"y":2819,"style":2825},"Last payment",[676,2833],{"x":2798,"y":2814,"width":2799,"height":2815,"rx":678,"fill":689,"stroke":690,"style":2822},[704,2835,108],{"x":2802,"y":2819,"style":2825},[676,2837],{"x":2806,"y":2814,"width":2807,"height":2815,"rx":678,"fill":689,"stroke":690,"style":2822},[704,2839,2840],{"x":2810,"y":2819,"style":2825},"Year fraction",[676,2842],{"x":2774,"y":2843,"width":2775,"height":2815,"rx":678,"fill":2777,"stroke":1613,"style":715},"68.0",[704,2845,154],{"x":2818,"y":2846,"style":2786},"87.0",[676,2848],{"x":2780,"y":2843,"width":2781,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2850,2852],{"x":2784,"y":2846,"style":2851},"font-size:10.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","L-01",[676,2854],{"x":2790,"y":2843,"width":2791,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2856,2857],{"x":2794,"y":2846,"style":2851},"2026-06-30",[676,2859],{"x":2798,"y":2843,"width":2799,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2861,2862],{"x":2802,"y":2846,"style":2851},"0 · 30\u002F360 US",[676,2864],{"x":2806,"y":2843,"width":2807,"height":2815,"rx":678,"fill":732,"stroke":733,"style":2822},[704,2866,2868],{"x":2810,"y":2846,"style":2867},"font-size:10.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","0.25",[676,2870],{"x":2774,"y":2871,"width":2775,"height":2815,"rx":678,"fill":2777,"stroke":1613,"style":715},"96.0",[704,2873,166],{"x":2818,"y":2874,"style":2786},"115.0",[676,2876],{"x":2780,"y":2871,"width":2781,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2878,2879],{"x":2784,"y":2874,"style":2851},"L-02",[676,2881],{"x":2790,"y":2871,"width":2791,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2883,2884],{"x":2794,"y":2874,"style":2851},"2026-08-31",[676,2886],{"x":2798,"y":2871,"width":2799,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2888,2889],{"x":2802,"y":2874,"style":2851},"4 · 30\u002F360 EU",[676,2891],{"x":2806,"y":2871,"width":2807,"height":2815,"rx":678,"fill":732,"stroke":733,"style":2822},[704,2893,2894],{"x":2810,"y":2874,"style":2867},"0.083333",[676,2896],{"x":2774,"y":2897,"width":2775,"height":2815,"rx":678,"fill":2777,"stroke":1613,"style":715},"124.0",[704,2899,179],{"x":2818,"y":2900,"style":2786},"143.0",[676,2902],{"x":2780,"y":2897,"width":2781,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2904,2905],{"x":2784,"y":2900,"style":2851},"L-03",[676,2907],{"x":2790,"y":2897,"width":2791,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2909,2910],{"x":2794,"y":2900,"style":2851},"2026-02-28",[676,2912],{"x":2798,"y":2897,"width":2799,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2914,2915],{"x":2802,"y":2900,"style":2851},"2 · act\u002F360",[676,2917],{"x":2806,"y":2897,"width":2807,"height":2815,"rx":678,"fill":732,"stroke":733,"style":2822},[704,2919,2920],{"x":2810,"y":2900,"style":2867},"0.594444",[676,2922],{"x":2774,"y":2923,"width":2775,"height":2815,"rx":678,"fill":2777,"stroke":1613,"style":715},"152.0",[704,2925,2020],{"x":2818,"y":2926,"style":2786},"171.0",[676,2928],{"x":2780,"y":2923,"width":2781,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2930,2931],{"x":2784,"y":2926,"style":2851},"L-04",[676,2933],{"x":2790,"y":2923,"width":2791,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2935,2936],{"x":2794,"y":2926,"style":2851},"2026-09-15",[676,2938],{"x":2798,"y":2923,"width":2799,"height":2815,"rx":678,"fill":681,"stroke":1613,"style":715},[704,2940,2941],{"x":2802,"y":2926,"style":2851},"1 · act\u002Fact",[676,2943],{"x":2806,"y":2923,"width":2807,"height":2815,"rx":678,"fill":732,"stroke":733,"style":2822},[704,2945,2946],{"x":2810,"y":2926,"style":2867},"0.041096",[47,2948,2950],{"id":2949},"common-pitfalls","Common pitfalls",[2952,2953,2954,2965,2971,2977,2983],"ul",{},[2955,2956,2957,2964],"li",{},[2958,2959,2960,2963],"strong",{},[14,2961,2962],{},"days \u002F 365"," for everything."," That is only basis 3; use the basis the contract specifies.",[2955,2966,2967,2970],{},[2958,2968,2969],{},"Treating YEARFRAC basis 0 as DAYS360\u002F360."," They differ when both dates are February month ends.",[2955,2972,2973,2976],{},[2958,2974,2975],{},"Forgetting leap-year month ends."," 28 February 2024 is not a month end; 29 February is.",[2955,2978,2979,2982],{},[2958,2980,2981],{},"Assuming basis 1 is ISDA actual\u002Factual."," Excel's rule averages year lengths for long periods.",[2955,2984,2985,2988],{},[2958,2986,2987],{},"No test on February and 31st dates."," Compare with spreadsheet results on those dates.",[47,2990,2992],{"id":2991},"conclusion","Conclusion",[10,2994,2995,2996,2998,2999,3001],{},"Excel's day-count functions are reproducible in a few lines once their rules are written down: ",[14,2997,20],{}," adjusts start and end days for 30-day months — capping at 30 in the European method, and in the US method treating any month-end start as the 30th and an end on the 31st as the 30th only when the start is 30 or later. ",[14,3000,16],{}," basis 0 is a slightly different US 30\u002F360 count, bases 2 and 3 divide actual days by 360 or 365, basis 4 is European 30\u002F360, and basis 1 uses 365 or 366 for periods up to a year and an average year length beyond. Test the functions against spreadsheet results on February and month-end dates before trusting them.",[47,3003,3005],{"id":3004},"frequently-asked-questions","Frequently asked questions",[10,3007,3008,3011],{},[2958,3009,3010],{},"What is the Python equivalent of Excel's YEARFRAC?","\nThere is no built-in function. Implement the day-count basis you need: actual\u002F360 and actual\u002F365 divide the day difference by 360 or 365, 30\u002F360 bases count 30-day months with month-end rules, and actual\u002Factual uses 365 or 366 days per year by Excel's rule.",[10,3013,3014,3017],{},[2958,3015,3016],{},"How do I calculate DAYS360 in Python?","\nCompute 360 times the year difference plus 30 times the month difference plus the difference of adjusted days. In the European method, cap both days at 30; in the US method, set a month-end start to 30 and an end of 31 to 30 only when the start day is 30 or more.",[10,3019,3020,3023],{},[2958,3021,3022],{},"Why does YEARFRAC with basis 0 not equal DAYS360 divided by 360?","\nYEARFRAC basis 0 adds a rule for February: when both dates are the last day of February, the end day also becomes 30, so the two results differ for such dates.",[10,3025,3026,3029],{},[2958,3027,3028],{},"Is Excel's actual\u002Factual the same as ISDA actual\u002Factual?","\nNo. Excel divides by 365 or 366 for periods up to a year and by an average year length for longer periods; ISDA splits the period by calendar year.",[47,3031,3033],{"id":3032},"related","Related",[2952,3035,3036,3042,3049,3056],{},[2955,3037,3038,3039,3041],{},"Up one level: ",[41,3040,44],{"href":43}," — spreadsheet functions in Python.",[2955,3043,3044,3048],{},[41,3045,3047],{"href":3046},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Feomonth-and-edate-equivalents-in-pandas\u002F","EOMONTH and EDATE Equivalents in pandas"," — month-end date arithmetic.",[2955,3050,3051,3055],{},[41,3052,3054],{"href":3053},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fxirr-and-xnpv-equivalents-in-python\u002F","XIRR and XNPV Equivalents in Python"," — dated cash flows, actual\u002F365.",[2955,3057,3058,3062],{},[41,3059,3061],{"href":3060},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fcalculate-age-and-tenure-from-dates-with-pandas\u002F","Calculate Age and Tenure from Dates with pandas"," — whole years and months between dates.",[3064,3065,3066],"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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":57,"searchDepth":232,"depth":232,"links":3068},[3069,3070,3071,3072,3073,3074,3075,3076,3077,3078,3079],{"id":49,"depth":232,"text":50},{"id":88,"depth":232,"text":89},{"id":204,"depth":232,"text":205},{"id":760,"depth":232,"text":761},{"id":1186,"depth":232,"text":1187},{"id":1700,"depth":232,"text":1701},{"id":2435,"depth":232,"text":2436},{"id":2949,"depth":232,"text":2950},{"id":2991,"depth":232,"text":2992},{"id":3004,"depth":232,"text":3005},{"id":3032,"depth":232,"text":3033},"2026-10-02","Reproduce Excel's YEARFRAC with all five day-count bases and DAYS360 with the US and European methods in Python: 30\u002F360 rules for month ends and February, actual\u002F360, actual\u002F365, actual\u002Factual with its leap-year logic, a vectorised version for interest accrual tables in pandas, and a check against spreadsheet results for the awkward dates.","md",[3084,3086,3088,3090],{"q":3010,"a":3085},"There is no built-in function. Implement the day-count basis you need: actual\u002F360 and actual\u002F365 divide the day difference by 360 or 365, 30\u002F360 bases count 30-day months with month-end rules, and actual\u002Factual uses 365 or 366 days per year by Excel's rule.",{"q":3016,"a":3087},"Compute 360 times the year difference plus 30 times the month difference plus the difference of adjusted days. In the European method, cap both days at 30; in the US method, set a month-end start to 30 and an end of 31 to 30 only when the start day is 30 or more.",{"q":3022,"a":3089},"YEARFRAC basis 0 adds a rule for February: when both dates are the last day of February, the end day also becomes 30, so the two results differ for such dates.",{"q":3028,"a":3091},"No. Excel divides by 365 or 366 for periods up to a year and by an average year length for longer periods; ISDA splits the period by calendar year.","yearfrac and days360 equivalents in python",null,{"breadcrumb":3095},[3096,3098,3101],{"name":3097,"item":957},"Home",{"name":3099,"item":3100},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":44,"item":43},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python",{"title":3104,"description":3105},"YEARFRAC and DAYS360 Equivalents in Python and pandas","Implement Excel YEARFRAC bases 0–4 and DAYS360 US and European in Python: 30\u002F360 month-end and February rules, actual\u002F360, actual\u002F365, actual\u002Factual, pandas accrual columns and a spreadsheet cross-check.","yearfrac-and-days360-equivalents-in-python","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python\u002Findex",[3109,3110],"formula-equivalents","dates-and-times","how-to","FMXPWSSTHYjWfbadYyt0pNSJgk6fHjJtwLk9aDN6qe0",[3114,3118],{"title":3115,"path":3116,"stem":3117,"children":-1},"XLOOKUP with Multiple Criteria in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fxlookup-with-multiple-criteria-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fxlookup-with-multiple-criteria-in-pandas\u002Findex",{"title":3119,"path":3120,"stem":3121,"children":-1},"Extracting Data from Messy Excel Sheets","\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets","advanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Findex",[3123,3125],{"path":3124,"title":44},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas",{"path":3126,"title":3099},"\u002Fadvanced-data-transformation-and-cleaning",[],[3129,3132,3135,3138,3141,3144],{"path":3130,"title":3131,"kind":3111},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fvlookup-equivalent-in-pandas-for-excel-files","The VLOOKUP Equivalent in pandas for Excel Files",{"path":3133,"title":3134,"kind":3111},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fextract-numbers-and-codes-from-text-with-pandas","Extract Numbers and Codes from Text with pandas",{"path":3136,"title":3137,"kind":3111},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fsplit-full-names-into-first-and-last-names-with-pandas","Split Full Names into First and Last Names with pandas",{"path":3139,"title":3140,"kind":3111},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fbin-values-into-ranges-with-pandas-cut","Bin Values into Ranges Like Excel FREQUENCY with pd.cut",{"path":3142,"title":3143,"kind":3111},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-percent-of-total-per-group-in-pandas","Calculate Percent of Total per Group in pandas",{"path":3145,"title":3146,"kind":3111},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcount-distinct-values-per-group-in-pandas","Count Distinct Values per Group in pandas",1790972090787]