[{"data":1,"prerenderedAt":2166},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fpmt-npv-and-irr-in-python":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fpmt-npv-and-irr-in-python":2132,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fpmt-npv-and-irr-in-python":2141,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fpmt-npv-and-irr-in-python":2146,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fpmt-npv-and-irr-in-python":2147},{"id":4,"title":5,"body":6,"dateModified":2098,"datePublished":2098,"description":2099,"extension":2100,"faq":2101,"keyword":2110,"kind":2111,"meta":2112,"navigation":331,"path":2120,"seo":2121,"slug":2124,"stem":2125,"topics":2126,"type":2130,"__hash__":2131},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fpmt-npv-and-irr-in-python\u002Findex.md","PMT, NPV, IRR and XIRR Excel Functions in Python",{"type":7,"value":8,"toc":2084},"minimark",[9,43,248,253,284,290,368,372,375,570,592,596,599,820,898,906,910,930,1038,1060,1177,1181,1190,1235,1246,1250,1258,1585,1608,1612,1615,1894,1897,1901,1955,1959,1973,1977,2019,2023,2029,2035,2041,2047,2051,2080],[10,11,12,13,17,18,21,22,25,26,21,29,32,33,36,37,42],"p",{},"Finance teams live in Excel's financial functions: ",[14,15,16],"code",{},"PMT"," for loan repayments, ",[14,19,20],{},"NPV"," and ",[14,23,24],{},"IRR"," for investment decisions, ",[14,27,28],{},"XNPV",[14,30,31],{},"XIRR"," for cash flows that do not arrive on neat anniversaries. When the calculation moves into a Python job — a model that runs over thousands of loans, a report that evaluates every project in a portfolio — the numbers must match the spreadsheets people already trust, to the cent. The ",[14,34,35],{},"numpy-financial"," package covers most of the functions with the same argument order as Excel. It also has one function whose definition differs from Excel's, and it lacks the dated versions entirely. This page, part of ",[38,39,41],"a",{"href":40},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002F","Excel Formula Equivalents in pandas",", covers all of them; every number below was checked against the same formulas recalculated in LibreOffice.",[44,45,53,54,53,58,53,62,53,70,53,78,53,85,53,92,53,95,53,99,53,104,53,110,53,112,53,117,53,119,53,130,53,133,53,137,53,139,53,143,53,145,53,150,53,153,53,157,53,159,53,163,53,165,53,170,53,173,53,177,53,179,53,183,53,185,53,190,53,193,53,197,53,199,53,203,53,205,53,208,53,211,53,215,53,217,53,221,53,223,53,228,53,231,53,235,53,237,53,241,53,243],"svg",{"viewBox":46,"role":47,"ariaLabelledBy":48,"xmlns":51,"style":52},"4 0 752 310","img",[49,50],"fin-map-t","fin-map-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  ",[55,56,57],"title",{"id":49},"Excel financial functions and their Python equivalents",[59,60,61],"desc",{"id":50},"A translation table. PMT, FV, NPER, RATE and IRR map directly to numpy_financial functions with the same arguments and sign conventions. Excel NPV corresponds to numpy_financial npv only when a zero is prepended, because Excel discounts the first value. XNPV and XIRR have no numpy_financial equivalent and need short helper functions.",[63,64],"rect",{"x":65,"y":66,"width":67,"height":68,"fill":69},"4","0","752","310","#ffffff",[63,71],{"x":72,"y":73,"width":74,"height":75,"rx":66,"fill":69,"stroke":76,"style":77},"20.0","16.0","146.3","40.0","var(--line,#cdd5e6)","stroke-width:1px",[63,79],{"x":80,"y":73,"width":81,"height":75,"rx":66,"fill":82,"stroke":83,"style":84},"166.3","286.8","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.2px",[86,87,91],"text",{"x":88,"y":89,"style":90},"309.7","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Python",[63,93],{"x":94,"y":73,"width":81,"height":75,"rx":66,"fill":82,"stroke":83,"style":84},"453.2",[86,96,98],{"x":97,"y":89,"style":90},"596.6","Matches Excel",[63,100],{"x":72,"y":101,"width":74,"height":102,"rx":66,"fill":103,"stroke":76,"style":77},"56.0","34.0","#e7ebef",[86,105,109],{"x":106,"y":107,"style":108},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","=PMT(r, n, pv)",[63,111],{"x":80,"y":101,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[86,113,116],{"x":88,"y":114,"style":115},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","npf.pmt(r, n, pv)",[63,118],{"x":94,"y":101,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[120,121,123],"g",{"transform":122},"translate(587.6,64.0) scale(0.750)",[124,125],"path",{"d":126,"fill":127,"stroke":128,"style":129},"M5 12.5l4.5 4.5L19 7","none","var(--teal,#0f9488)","stroke-width:2.40px;stroke-linecap:round;stroke-linejoin:round",[63,131],{"x":72,"y":132,"width":74,"height":102,"rx":66,"fill":103,"stroke":76,"style":77},"90.0",[86,134,136],{"x":106,"y":135,"style":108},"112.0","=FV(r, n, pmt, pv)",[63,138],{"x":80,"y":132,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[86,140,142],{"x":88,"y":141,"style":115},"111.0","npf.fv(r, n, pmt, pv)",[63,144],{"x":94,"y":132,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[120,146,148],{"transform":147},"translate(587.6,98.0) scale(0.750)",[124,149],{"d":126,"fill":127,"stroke":128,"style":129},[63,151],{"x":72,"y":152,"width":74,"height":102,"rx":66,"fill":103,"stroke":76,"style":77},"124.0",[86,154,156],{"x":106,"y":155,"style":108},"146.0","=NPER(r, pmt, pv)",[63,158],{"x":80,"y":152,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[86,160,162],{"x":88,"y":161,"style":115},"145.0","npf.nper(r, pmt, pv)",[63,164],{"x":94,"y":152,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[120,166,168],{"transform":167},"translate(587.6,132.0) scale(0.750)",[124,169],{"d":126,"fill":127,"stroke":128,"style":129},[63,171],{"x":72,"y":172,"width":74,"height":102,"rx":66,"fill":103,"stroke":76,"style":77},"158.0",[86,174,176],{"x":106,"y":175,"style":108},"180.0","=RATE(n, pmt, pv)",[63,178],{"x":80,"y":172,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[86,180,182],{"x":88,"y":181,"style":115},"179.0","npf.rate(n, pmt, pv, 0)",[63,184],{"x":94,"y":172,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[120,186,188],{"transform":187},"translate(587.6,166.0) scale(0.750)",[124,189],{"d":126,"fill":127,"stroke":128,"style":129},[63,191],{"x":72,"y":192,"width":74,"height":102,"rx":66,"fill":103,"stroke":76,"style":77},"192.0",[86,194,196],{"x":106,"y":195,"style":108},"214.0","=NPV(r, v1, v2, …)",[63,198],{"x":80,"y":192,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[86,200,202],{"x":88,"y":201,"style":115},"213.0","npf.npv(r, [0, v1, v2, …])",[63,204],{"x":94,"y":192,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[86,206,207],{"x":97,"y":201,"style":115},"only with the leading 0",[63,209],{"x":72,"y":210,"width":74,"height":102,"rx":66,"fill":103,"stroke":76,"style":77},"226.0",[86,212,214],{"x":106,"y":213,"style":108},"248.0","=IRR(values)",[63,216],{"x":80,"y":210,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[86,218,220],{"x":88,"y":219,"style":115},"247.0","npf.irr(values)",[63,222],{"x":94,"y":210,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[120,224,226],{"transform":225},"translate(587.6,234.0) scale(0.750)",[124,227],{"d":126,"fill":127,"stroke":128,"style":129},[63,229],{"x":72,"y":230,"width":74,"height":102,"rx":66,"fill":103,"stroke":76,"style":77},"260.0",[86,232,234],{"x":106,"y":233,"style":108},"282.0","=XNPV \u002F =XIRR",[63,236],{"x":80,"y":230,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[86,238,240],{"x":88,"y":239,"style":115},"281.0","helper functions below",[63,242],{"x":94,"y":230,"width":81,"height":102,"rx":66,"fill":69,"stroke":76,"style":77},[120,244,246],{"transform":245},"translate(587.6,268.0) scale(0.750)",[124,247],{"d":126,"fill":127,"stroke":128,"style":129},[249,250,252],"h2",{"id":251},"prerequisites","Prerequisites",[254,255,260],"pre",{"className":256,"code":257,"language":258,"meta":259,"style":259},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install numpy-financial pandas openpyxl\n","bash","",[14,261,262],{"__ignoreMap":259},[263,264,267,271,275,278,281],"span",{"class":265,"line":266},"line",1,[263,268,270],{"class":269},"sMTad","pip",[263,272,274],{"class":273},"srMev"," install",[263,276,277],{"class":273}," numpy-financial",[263,279,280],{"class":273}," pandas",[263,282,283],{"class":273}," openpyxl\n",[10,285,286,289],{},[14,287,288],{},"project.xlsx"," holds a project's cash flows: an investment of 10,000 on 1 January 2026 and three inflows on irregular dates.",[254,291,295],{"className":292,"code":293,"language":294,"meta":259,"style":259},"language-python shiki shiki-themes github-light github-dark-high-contrast","import numpy_financial as npf\nimport pandas as pd\n\nproject = pd.read_excel(\"project.xlsx\")\nflows = project[\"cash_flow\"].tolist()\n","python",[14,296,297,313,326,333,351],{"__ignoreMap":259},[263,298,299,303,307,310],{"class":265,"line":266},[263,300,302],{"class":301},"s-kum","import",[263,304,306],{"class":305},"skGVy"," numpy_financial ",[263,308,309],{"class":301},"as",[263,311,312],{"class":305}," npf\n",[263,314,316,318,321,323],{"class":265,"line":315},2,[263,317,302],{"class":301},[263,319,320],{"class":305}," pandas ",[263,322,309],{"class":301},[263,324,325],{"class":305}," pd\n",[263,327,329],{"class":265,"line":328},3,[263,330,332],{"emptyLinePlaceholder":331},true,"\n",[263,334,336,339,342,345,348],{"class":265,"line":335},4,[263,337,338],{"class":305},"project ",[263,340,341],{"class":301},"=",[263,343,344],{"class":305}," pd.read_excel(",[263,346,347],{"class":273},"\"project.xlsx\"",[263,349,350],{"class":305},")\n",[263,352,354,357,359,362,365],{"class":265,"line":353},5,[263,355,356],{"class":305},"flows ",[263,358,341],{"class":301},[263,360,361],{"class":305}," project[",[263,363,364],{"class":273},"\"cash_flow\"",[263,366,367],{"class":305},"].tolist()\n",[249,369,371],{"id":370},"step-1-loan-payments-pmt-fv-nper-and-rate","Step 1 — Loan payments: PMT, FV, NPER and RATE",[10,373,374],{},"These map one to one, with the same arguments in the same order. Rates and periods must agree: for monthly payments, divide the annual rate by 12 and count months:",[254,376,378],{"className":292,"code":377,"language":294,"meta":259,"style":259},"payment = npf.pmt(0.05 \u002F 12, 360, 200_000)\nprint(round(payment, 2))                               # =PMT(5%\u002F12, 360, 200000)\n\nprint(round(npf.fv(0.04 \u002F 12, 120, -500, 0), 2))       # save 500 a month for 10 years at 4%\nprint(round(npf.nper(0.05 \u002F 12, -1500, 200_000), 1))   # months to repay at 1,500 a month\nprint(round(npf.rate(360, -1073.64, 200_000, 0) * 12, 4))\n",[14,379,380,413,437,441,487,526],{"__ignoreMap":259},[263,381,382,385,387,390,394,397,400,403,406,408,411],{"class":265,"line":266},[263,383,384],{"class":305},"payment ",[263,386,341],{"class":301},[263,388,389],{"class":305}," npf.pmt(",[263,391,393],{"class":392},"sP0c6","0.05",[263,395,396],{"class":301}," \u002F",[263,398,399],{"class":392}," 12",[263,401,402],{"class":305},", ",[263,404,405],{"class":392},"360",[263,407,402],{"class":305},[263,409,410],{"class":392},"200_000",[263,412,350],{"class":305},[263,414,415,418,421,424,427,430,433],{"class":265,"line":315},[263,416,417],{"class":392},"print",[263,419,420],{"class":305},"(",[263,422,423],{"class":392},"round",[263,425,426],{"class":305},"(payment, ",[263,428,429],{"class":392},"2",[263,431,432],{"class":305},"))                               ",[263,434,436],{"class":435},"s-wDw","# =PMT(5%\u002F12, 360, 200000)\n",[263,438,439],{"class":265,"line":328},[263,440,332],{"emptyLinePlaceholder":331},[263,442,443,445,447,449,452,455,457,459,461,464,466,469,472,474,476,479,481,484],{"class":265,"line":335},[263,444,417],{"class":392},[263,446,420],{"class":305},[263,448,423],{"class":392},[263,450,451],{"class":305},"(npf.fv(",[263,453,454],{"class":392},"0.04",[263,456,396],{"class":301},[263,458,399],{"class":392},[263,460,402],{"class":305},[263,462,463],{"class":392},"120",[263,465,402],{"class":305},[263,467,468],{"class":301},"-",[263,470,471],{"class":392},"500",[263,473,402],{"class":305},[263,475,66],{"class":392},[263,477,478],{"class":305},"), ",[263,480,429],{"class":392},[263,482,483],{"class":305},"))       ",[263,485,486],{"class":435},"# save 500 a month for 10 years at 4%\n",[263,488,489,491,493,495,498,500,502,504,506,508,511,513,515,517,520,523],{"class":265,"line":353},[263,490,417],{"class":392},[263,492,420],{"class":305},[263,494,423],{"class":392},[263,496,497],{"class":305},"(npf.nper(",[263,499,393],{"class":392},[263,501,396],{"class":301},[263,503,399],{"class":392},[263,505,402],{"class":305},[263,507,468],{"class":301},[263,509,510],{"class":392},"1500",[263,512,402],{"class":305},[263,514,410],{"class":392},[263,516,478],{"class":305},[263,518,519],{"class":392},"1",[263,521,522],{"class":305},"))   ",[263,524,525],{"class":435},"# months to repay at 1,500 a month\n",[263,527,529,531,533,535,538,540,542,544,547,549,551,553,555,558,561,563,565,567],{"class":265,"line":528},6,[263,530,417],{"class":392},[263,532,420],{"class":305},[263,534,423],{"class":392},[263,536,537],{"class":305},"(npf.rate(",[263,539,405],{"class":392},[263,541,402],{"class":305},[263,543,468],{"class":301},[263,545,546],{"class":392},"1073.64",[263,548,402],{"class":305},[263,550,410],{"class":392},[263,552,402],{"class":305},[263,554,66],{"class":392},[263,556,557],{"class":305},") ",[263,559,560],{"class":301},"*",[263,562,399],{"class":392},[263,564,402],{"class":305},[263,566,65],{"class":392},[263,568,569],{"class":305},"))\n",[10,571,572,573,576,577,580,581,584,585,588,589,591],{},"The payment is ",[14,574,575],{},"-1073.64",": the loan is received (positive), so the payments are negative, exactly as in Excel. Saving 500 a month grows to 73,624.90; paying 1,500 a month clears the loan in 195 months; and ",[14,578,579],{},"rate"," recovers the 5% annual rate from the payment. ",[14,582,583],{},"npf.rate"," needs the future value as an explicit fourth argument, where Excel's ",[14,586,587],{},"RATE"," defaults it to zero — pass ",[14,590,66],{},".",[249,593,595],{"id":594},"step-2-loan-schedules-for-a-whole-portfolio","Step 2 — Loan schedules for a whole portfolio",[10,597,598],{},"The functions accept arrays, so a DataFrame of loans gets its payments in one call — the reason to move this into Python at all:",[254,600,602],{"className":292,"code":601,"language":294,"meta":259,"style":259},"loans = pd.DataFrame({\"loan\": [\"L1\", \"L2\", \"L3\"], \"principal\": [200_000, 150_000, 320_000],\n                      \"annual_rate\": [0.05, 0.043, 0.061], \"years\": [30, 25, 20]})\nloans[\"monthly_payment\"] = -npf.pmt(loans[\"annual_rate\"] \u002F 12, loans[\"years\"] * 12, loans[\"principal\"])\nloans[\"total_interest\"] = loans[\"monthly_payment\"] * loans[\"years\"] * 12 - loans[\"principal\"]\nprint(loans.round({\"monthly_payment\": 2, \"total_interest\": 2}).to_string(index=False))\n",[14,603,604,656,698,745,784],{"__ignoreMap":259},[263,605,606,609,611,614,617,620,623,625,628,630,633,636,639,641,643,645,648,650,653],{"class":265,"line":266},[263,607,608],{"class":305},"loans ",[263,610,341],{"class":301},[263,612,613],{"class":305}," pd.DataFrame({",[263,615,616],{"class":273},"\"loan\"",[263,618,619],{"class":305},": [",[263,621,622],{"class":273},"\"L1\"",[263,624,402],{"class":305},[263,626,627],{"class":273},"\"L2\"",[263,629,402],{"class":305},[263,631,632],{"class":273},"\"L3\"",[263,634,635],{"class":305},"], ",[263,637,638],{"class":273},"\"principal\"",[263,640,619],{"class":305},[263,642,410],{"class":392},[263,644,402],{"class":305},[263,646,647],{"class":392},"150_000",[263,649,402],{"class":305},[263,651,652],{"class":392},"320_000",[263,654,655],{"class":305},"],\n",[263,657,658,661,663,665,667,670,672,675,677,680,682,685,687,690,692,695],{"class":265,"line":315},[263,659,660],{"class":273},"                      \"annual_rate\"",[263,662,619],{"class":305},[263,664,393],{"class":392},[263,666,402],{"class":305},[263,668,669],{"class":392},"0.043",[263,671,402],{"class":305},[263,673,674],{"class":392},"0.061",[263,676,635],{"class":305},[263,678,679],{"class":273},"\"years\"",[263,681,619],{"class":305},[263,683,684],{"class":392},"30",[263,686,402],{"class":305},[263,688,689],{"class":392},"25",[263,691,402],{"class":305},[263,693,694],{"class":392},"20",[263,696,697],{"class":305},"]})\n",[263,699,700,703,706,709,711,714,717,720,722,725,727,730,732,734,736,738,740,742],{"class":265,"line":328},[263,701,702],{"class":305},"loans[",[263,704,705],{"class":273},"\"monthly_payment\"",[263,707,708],{"class":305},"] ",[263,710,341],{"class":301},[263,712,713],{"class":301}," -",[263,715,716],{"class":305},"npf.pmt(loans[",[263,718,719],{"class":273},"\"annual_rate\"",[263,721,708],{"class":305},[263,723,724],{"class":301},"\u002F",[263,726,399],{"class":392},[263,728,729],{"class":305},", loans[",[263,731,679],{"class":273},[263,733,708],{"class":305},[263,735,560],{"class":301},[263,737,399],{"class":392},[263,739,729],{"class":305},[263,741,638],{"class":273},[263,743,744],{"class":305},"])\n",[263,746,747,749,752,754,756,759,761,763,765,767,769,771,773,775,777,779,781],{"class":265,"line":335},[263,748,702],{"class":305},[263,750,751],{"class":273},"\"total_interest\"",[263,753,708],{"class":305},[263,755,341],{"class":301},[263,757,758],{"class":305}," loans[",[263,760,705],{"class":273},[263,762,708],{"class":305},[263,764,560],{"class":301},[263,766,758],{"class":305},[263,768,679],{"class":273},[263,770,708],{"class":305},[263,772,560],{"class":301},[263,774,399],{"class":392},[263,776,713],{"class":301},[263,778,758],{"class":305},[263,780,638],{"class":273},[263,782,783],{"class":305},"]\n",[263,785,786,788,791,793,796,798,800,802,804,806,809,813,815,818],{"class":265,"line":353},[263,787,417],{"class":392},[263,789,790],{"class":305},"(loans.round({",[263,792,705],{"class":273},[263,794,795],{"class":305},": ",[263,797,429],{"class":392},[263,799,402],{"class":305},[263,801,751],{"class":273},[263,803,795],{"class":305},[263,805,429],{"class":392},[263,807,808],{"class":305},"}).to_string(",[263,810,812],{"class":811},"sa561","index",[263,814,341],{"class":301},[263,816,817],{"class":392},"False",[263,819,569],{"class":305},[44,821,53,827,53,830,53,833,53,837,53,843,53,849,53,855,53,860,53,865,53,868,53,872,53,876,53,881,53,884,53,888,53,893],{"viewBox":822,"role":47,"ariaLabelledBy":823,"xmlns":51,"style":826},"4 0 744 172",[824,825],"fin-interest-t","fin-interest-d","width:100%;max-width:744px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[55,828,829],{"id":824},"Monthly payment and total interest for three loans",[59,831,832],{"id":825},"Bars for three loans computed with npf.pmt. L1, 200,000 over 30 years at 5%, pays 1,073.64 a month and 186,512 in total interest. L2, 150,000 over 25 years at 4.3%, pays 816.81 and 95,044 in interest. L3, 320,000 over 20 years at 6.1%, pays 2,311.08 and 234,659 in interest.",[63,834],{"x":65,"y":66,"width":835,"height":836,"fill":69},"744","172",[86,838,842],{"x":839,"y":840,"style":841},"380.0","31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Total interest over the life of each loan",[63,844],{"x":72,"y":845,"width":846,"height":847,"rx":848,"fill":103,"stroke":76,"style":77},"48.0","159.6","28.0","7",[86,850,854],{"x":851,"y":852,"style":853},"99.8","66.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","L1 · 200k, 30y, 5%",[63,856],{"x":857,"y":845,"width":858,"height":847,"rx":848,"fill":82,"stroke":83,"style":859},"189.6","382.6","stroke-width:1.5px",[86,861,864],{"x":862,"y":852,"style":863},"580.2","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:start","186,512",[63,866],{"x":72,"y":867,"width":846,"height":847,"rx":848,"fill":103,"stroke":76,"style":77},"88.0",[86,869,871],{"x":851,"y":870,"style":853},"106.5","L2 · 150k, 25y, 4.3%",[63,873],{"x":857,"y":867,"width":874,"height":847,"rx":848,"fill":875,"stroke":128,"style":859},"195.0","#d9f4f1",[86,877,880],{"x":878,"y":870,"style":879},"392.6","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","95,044",[63,882],{"x":72,"y":883,"width":846,"height":847,"rx":848,"fill":103,"stroke":76,"style":77},"128.0",[86,885,887],{"x":851,"y":886,"style":853},"146.5","L3 · 320k, 20y, 6.1%",[63,889],{"x":857,"y":883,"width":890,"height":847,"rx":848,"fill":891,"stroke":892,"style":859},"481.4","#fdefd8","var(--gold,#b4740a)",[86,894,897],{"x":895,"y":886,"style":896},"679.0","font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start","234,659",[10,899,900,901,905],{},"Negating the result gives payments as positive amounts for a report; keep the signed version if the figures feed further cash-flow calculations. ",[38,902,904],{"href":903},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-budget-vs-actual-variance-report\u002F","Build a Budget vs Actual Variance Report"," shows these figures in a finished report.",[249,907,909],{"id":908},"step-3-npv-the-difference-that-catches-everyone","Step 3 — NPV: the difference that catches everyone",[10,911,912,913,916,917,921,922,925,926,929],{},"Excel's ",[14,914,915],{},"NPV(rate, v1, v2, …)"," assumes the ",[918,919,920],"em",{},"first"," value arrives one period from now, so it discounts every value. ",[14,923,924],{},"numpy_financial.npv(rate, values)"," assumes the first value arrives ",[918,927,928],{},"today"," and leaves it undiscounted. Given the same list, they disagree:",[254,931,933],{"className":292,"code":932,"language":294,"meta":259,"style":259},"print(round(npf.npv(0.10, flows), 2))            # first flow at t = 0\nprint(round(npf.npv(0.10, [0] + flows), 2))      # = Excel =NPV(10%, A2:A5)\nprint(round(flows[0] + npf.npv(0.10, [0] + flows[1:]), 2))   # = Excel =A2 + NPV(10%, A3:A5)\n",[14,934,935,960,993],{"__ignoreMap":259},[263,936,937,939,941,943,946,949,952,954,957],{"class":265,"line":266},[263,938,417],{"class":392},[263,940,420],{"class":305},[263,942,423],{"class":392},[263,944,945],{"class":305},"(npf.npv(",[263,947,948],{"class":392},"0.10",[263,950,951],{"class":305},", flows), ",[263,953,429],{"class":392},[263,955,956],{"class":305},"))            ",[263,958,959],{"class":435},"# first flow at t = 0\n",[263,961,962,964,966,968,970,972,975,977,979,982,985,987,990],{"class":265,"line":315},[263,963,417],{"class":392},[263,965,420],{"class":305},[263,967,423],{"class":392},[263,969,945],{"class":305},[263,971,948],{"class":392},[263,973,974],{"class":305},", [",[263,976,66],{"class":392},[263,978,708],{"class":305},[263,980,981],{"class":301},"+",[263,983,984],{"class":305}," flows), ",[263,986,429],{"class":392},[263,988,989],{"class":305},"))      ",[263,991,992],{"class":435},"# = Excel =NPV(10%, A2:A5)\n",[263,994,995,997,999,1001,1004,1006,1008,1010,1013,1015,1017,1019,1021,1023,1026,1028,1031,1033,1035],{"class":265,"line":328},[263,996,417],{"class":392},[263,998,420],{"class":305},[263,1000,423],{"class":392},[263,1002,1003],{"class":305},"(flows[",[263,1005,66],{"class":392},[263,1007,708],{"class":305},[263,1009,981],{"class":301},[263,1011,1012],{"class":305}," npf.npv(",[263,1014,948],{"class":392},[263,1016,974],{"class":305},[263,1018,66],{"class":392},[263,1020,708],{"class":305},[263,1022,981],{"class":301},[263,1024,1025],{"class":305}," flows[",[263,1027,519],{"class":392},[263,1029,1030],{"class":305},":]), ",[263,1032,429],{"class":392},[263,1034,522],{"class":305},[263,1036,1037],{"class":435},"# = Excel =A2 + NPV(10%, A3:A5)\n",[10,1039,1040,1041,1044,1045,1048,1049,1052,1053,1056,1057,1059],{},"Recalculated in LibreOffice, ",[14,1042,1043],{},"=NPV(10%, A2:A5)"," gives 1,188.44 and ",[14,1046,1047],{},"=A2+NPV(10%, A3:A5)"," gives 1,307.29. The first matches ",[14,1050,1051],{},"npf.npv"," with a zero prepended; the second — the ",[918,1054,1055],{},"correct"," way to value a project with an investment today — matches ",[14,1058,1051],{}," on the flows as they are. Many spreadsheets use the first form by mistake, discounting today's investment by a year. When porting a model, check which form the workbook uses and reproduce it deliberately, then fix it if it is the mistake.",[44,1061,53,1066,53,1069,53,1072,53,1075,53,1082,53,1088,53,1093,53,1098,53,1101,53,1105,53,1109,53,1115,53,1119,53,1123,53,1125,53,1127,53,1130,53,1134,53,1136,53,1138,53,1141,53,1146,53,1148,53,1150,53,1155,53,1161,53,1165,53,1169,53,1173,53,1175],{"viewBox":1062,"role":47,"ariaLabelledBy":1063,"xmlns":51,"style":52},"4 0 752 278",[1064,1065],"fin-npv-t","fin-npv-d",[55,1067,1068],{"id":1064},"How Excel NPV and numpy-financial npv place the cash flows",[59,1070,1071],{"id":1065},"A timeline of four cash flows: -10,000, 3,000, 4,200 and 6,800. numpy_financial npv places the first flow at today, undiscounted, giving 1,307.29. Excel's NPV over all four places the first flow one period out, discounting it too, giving 1,188.44. The correct project value, with the investment today, is 1,307.29.",[63,1073],{"x":65,"y":66,"width":67,"height":1074,"fill":69},"278",[265,1076],{"x1":1077,"y1":1078,"x2":1079,"y2":1078,"stroke":1080,"style":1081},"76.0","139.0","684.0","var(--muted,#5b6780)","stroke-width:2.5px",[63,1083],{"x":72,"y":73,"width":1084,"height":1085,"rx":1086,"fill":82,"stroke":83,"style":1087},"136.0","91.0","12","stroke-width:2px",[86,1089,1092],{"x":867,"y":1090,"style":1091},"57.2","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","t = 0: −10,000",[86,1094,1097],{"x":867,"y":1095,"style":1096},"75.8","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","npf.npv: today",[265,1099],{"x1":867,"y1":1100,"x2":867,"y2":1078,"stroke":1080,"style":859},"107.0",[1102,1103],"circle",{"cx":867,"cy":1078,"r":1104,"fill":83,"stroke":69,"style":1087},"7.0",[63,1106],{"x":1107,"y":1108,"width":1084,"height":1085,"rx":1086,"fill":891,"stroke":892,"style":1087},"166.0","171.0",[86,1110,1114],{"x":1111,"y":1112,"style":1113},"234.0","203.8","font-size:12.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","t = 1: 3,000",[86,1116,1118],{"x":1111,"y":1117,"style":1096},"222.2","Excel NPV starts",[86,1120,1122],{"x":1111,"y":1121,"style":1096},"239.2","here",[265,1124],{"x1":1111,"y1":1078,"x2":1111,"y2":1108,"stroke":1080,"style":859},[1102,1126],{"cx":1111,"cy":1078,"r":1104,"fill":892,"stroke":69,"style":1087},[63,1128],{"x":1129,"y":73,"width":1084,"height":1085,"rx":1086,"fill":103,"stroke":76,"style":1087},"312.0",[86,1131,1133],{"x":839,"y":1132,"style":853},"65.8","t = 2: 4,200",[265,1135],{"x1":839,"y1":1100,"x2":839,"y2":1078,"stroke":1080,"style":859},[1102,1137],{"cx":839,"cy":1078,"r":1104,"fill":1080,"stroke":69,"style":1087},[63,1139],{"x":1140,"y":1108,"width":1084,"height":1085,"rx":1086,"fill":103,"stroke":76,"style":1087},"458.0",[86,1142,1145],{"x":1143,"y":1144,"style":853},"526.0","220.8","t = 3: 6,800",[265,1147],{"x1":1143,"y1":1078,"x2":1143,"y2":1108,"stroke":1080,"style":859},[1102,1149],{"cx":1143,"cy":1078,"r":1104,"fill":1080,"stroke":69,"style":1087},[63,1151],{"x":1152,"y":73,"width":1084,"height":1085,"rx":1086,"fill":1153,"stroke":1154,"style":1087},"604.0","#fee8f2","var(--accent,#f43f8f)",[86,1156,1160],{"x":1157,"y":1158,"style":1159},"672.0","40.5","font-size:12.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","npf.npv →",[86,1162,1164],{"x":1157,"y":1163,"style":1159},"57.0","1,307.29",[86,1166,1168],{"x":1157,"y":1167,"style":1096},"75.5","Excel NPV(all four)",[86,1170,1172],{"x":1157,"y":1171,"style":1096},"92.5","→ 1,188.44",[265,1174],{"x1":1157,"y1":1100,"x2":1157,"y2":1078,"stroke":1080,"style":859},[1102,1176],{"cx":1157,"cy":1078,"r":1104,"fill":1154,"stroke":69,"style":1087},[249,1178,1180],{"id":1179},"step-4-irr","Step 4 — IRR",[10,1182,1183,1186,1187,1189],{},[14,1184,1185],{},"npf.irr"," matches Excel's ",[14,1188,24],{},": the rate at which the NPV of the flows, first one today, is zero:",[254,1191,1193],{"className":292,"code":1192,"language":294,"meta":259,"style":259},"irr = npf.irr(flows)\nprint(round(irr, 6), \"| check:\", round(npf.npv(irr, flows), 6))\n",[14,1194,1195,1205],{"__ignoreMap":259},[263,1196,1197,1200,1202],{"class":265,"line":266},[263,1198,1199],{"class":305},"irr ",[263,1201,341],{"class":301},[263,1203,1204],{"class":305}," npf.irr(flows)\n",[263,1206,1207,1209,1211,1213,1216,1219,1221,1224,1226,1228,1231,1233],{"class":265,"line":315},[263,1208,417],{"class":392},[263,1210,420],{"class":305},[263,1212,423],{"class":392},[263,1214,1215],{"class":305},"(irr, ",[263,1217,1218],{"class":392},"6",[263,1220,478],{"class":305},[263,1222,1223],{"class":273},"\"| check:\"",[263,1225,402],{"class":305},[263,1227,423],{"class":392},[263,1229,1230],{"class":305},"(npf.npv(irr, flows), ",[263,1232,1218],{"class":392},[263,1234,569],{"class":305},[10,1236,1237,1238,1241,1242,1245],{},"The rate is 16.34%, and the check confirms that discounting at that rate gives an NPV of essentially zero. Like Excel, ",[14,1239,1240],{},"irr"," needs at least one sign change in the flows and can return ",[14,1243,1244],{},"nan"," when no rate solves the equation; unusual flow patterns with several sign changes can have more than one valid IRR, which is a property of the maths rather than of either tool.",[249,1247,1249],{"id":1248},"step-5-xnpv-and-xirr-for-dated-cash-flows","Step 5 — XNPV and XIRR for dated cash flows",[10,1251,1252,1253,21,1255,1257],{},"Real projects are paid on real dates, and Excel's ",[14,1254,28],{},[14,1256,31],{}," discount each flow by the actual number of days since the first one, over a 365-day year. numpy-financial does not include them, but both are a few lines:",[254,1259,1261],{"className":292,"code":1260,"language":294,"meta":259,"style":259},"def xnpv(rate, cash_flows, dates):\n    start = dates[0]\n    return sum(cf \u002F (1 + rate) ** ((d - start).days \u002F 365) for cf, d in zip(cash_flows, dates))\n\ndef xirr(cash_flows, dates, low=-0.99, high=10.0, tol=1e-10):\n    f = lambda r: xnpv(r, cash_flows, dates)\n    if f(low) * f(high) > 0:\n        raise ValueError(\"no rate in range makes XNPV zero\")\n    while high - low > tol:\n        mid = (low + high) \u002F 2\n        low, high = (low, mid) if f(low) * f(mid) \u003C= 0 else (mid, high)\n    return (low + high) \u002F 2\n\ndates = [d.date() for d in project[\"date\"]]\nprint(round(xnpv(0.10, flows, dates), 2), round(xirr(flows, dates), 6))\n",[14,1262,1263,1275,1289,1346,1350,1385,1398,1421,1437,1456,1477,1509,1524,1529,1555],{"__ignoreMap":259},[263,1264,1265,1268,1272],{"class":265,"line":266},[263,1266,1267],{"class":301},"def",[263,1269,1271],{"class":1270},"s_Opv"," xnpv",[263,1273,1274],{"class":305},"(rate, cash_flows, dates):\n",[263,1276,1277,1280,1282,1285,1287],{"class":265,"line":315},[263,1278,1279],{"class":305},"    start ",[263,1281,341],{"class":301},[263,1283,1284],{"class":305}," dates[",[263,1286,66],{"class":392},[263,1288,783],{"class":305},[263,1290,1291,1294,1297,1300,1302,1305,1307,1310,1313,1316,1319,1321,1324,1326,1329,1331,1334,1337,1340,1343],{"class":265,"line":328},[263,1292,1293],{"class":301},"    return",[263,1295,1296],{"class":392}," sum",[263,1298,1299],{"class":305},"(cf ",[263,1301,724],{"class":301},[263,1303,1304],{"class":305}," (",[263,1306,519],{"class":392},[263,1308,1309],{"class":301}," +",[263,1311,1312],{"class":305}," rate) ",[263,1314,1315],{"class":301},"**",[263,1317,1318],{"class":305}," ((d ",[263,1320,468],{"class":301},[263,1322,1323],{"class":305}," start).days ",[263,1325,724],{"class":301},[263,1327,1328],{"class":392}," 365",[263,1330,557],{"class":305},[263,1332,1333],{"class":301},"for",[263,1335,1336],{"class":305}," cf, d ",[263,1338,1339],{"class":301},"in",[263,1341,1342],{"class":392}," zip",[263,1344,1345],{"class":305},"(cash_flows, dates))\n",[263,1347,1348],{"class":265,"line":335},[263,1349,332],{"emptyLinePlaceholder":331},[263,1351,1352,1354,1357,1360,1363,1366,1369,1371,1374,1377,1379,1382],{"class":265,"line":353},[263,1353,1267],{"class":301},[263,1355,1356],{"class":1270}," xirr",[263,1358,1359],{"class":305},"(cash_flows, dates, low",[263,1361,1362],{"class":301},"=-",[263,1364,1365],{"class":392},"0.99",[263,1367,1368],{"class":305},", high",[263,1370,341],{"class":301},[263,1372,1373],{"class":392},"10.0",[263,1375,1376],{"class":305},", tol",[263,1378,341],{"class":301},[263,1380,1381],{"class":392},"1e-10",[263,1383,1384],{"class":305},"):\n",[263,1386,1387,1390,1392,1395],{"class":265,"line":528},[263,1388,1389],{"class":305},"    f ",[263,1391,341],{"class":301},[263,1393,1394],{"class":301}," lambda",[263,1396,1397],{"class":305}," r: xnpv(r, cash_flows, dates)\n",[263,1399,1401,1404,1407,1409,1412,1415,1418],{"class":265,"line":1400},7,[263,1402,1403],{"class":301},"    if",[263,1405,1406],{"class":305}," f(low) ",[263,1408,560],{"class":301},[263,1410,1411],{"class":305}," f(high) ",[263,1413,1414],{"class":301},">",[263,1416,1417],{"class":392}," 0",[263,1419,1420],{"class":305},":\n",[263,1422,1424,1427,1430,1432,1435],{"class":265,"line":1423},8,[263,1425,1426],{"class":301},"        raise",[263,1428,1429],{"class":392}," ValueError",[263,1431,420],{"class":305},[263,1433,1434],{"class":273},"\"no rate in range makes XNPV zero\"",[263,1436,350],{"class":305},[263,1438,1440,1443,1446,1448,1451,1453],{"class":265,"line":1439},9,[263,1441,1442],{"class":301},"    while",[263,1444,1445],{"class":305}," high ",[263,1447,468],{"class":301},[263,1449,1450],{"class":305}," low ",[263,1452,1414],{"class":301},[263,1454,1455],{"class":305}," tol:\n",[263,1457,1459,1462,1464,1467,1469,1472,1474],{"class":265,"line":1458},10,[263,1460,1461],{"class":305},"        mid ",[263,1463,341],{"class":301},[263,1465,1466],{"class":305}," (low ",[263,1468,981],{"class":301},[263,1470,1471],{"class":305}," high) ",[263,1473,724],{"class":301},[263,1475,1476],{"class":392}," 2\n",[263,1478,1480,1483,1485,1488,1491,1493,1495,1498,1501,1503,1506],{"class":265,"line":1479},11,[263,1481,1482],{"class":305},"        low, high ",[263,1484,341],{"class":301},[263,1486,1487],{"class":305}," (low, mid) ",[263,1489,1490],{"class":301},"if",[263,1492,1406],{"class":305},[263,1494,560],{"class":301},[263,1496,1497],{"class":305}," f(mid) ",[263,1499,1500],{"class":301},"\u003C=",[263,1502,1417],{"class":392},[263,1504,1505],{"class":301}," else",[263,1507,1508],{"class":305}," (mid, high)\n",[263,1510,1512,1514,1516,1518,1520,1522],{"class":265,"line":1511},12,[263,1513,1293],{"class":301},[263,1515,1466],{"class":305},[263,1517,981],{"class":301},[263,1519,1471],{"class":305},[263,1521,724],{"class":301},[263,1523,1476],{"class":392},[263,1525,1527],{"class":265,"line":1526},13,[263,1528,332],{"emptyLinePlaceholder":331},[263,1530,1532,1535,1537,1540,1542,1545,1547,1549,1552],{"class":265,"line":1531},14,[263,1533,1534],{"class":305},"dates ",[263,1536,341],{"class":301},[263,1538,1539],{"class":305}," [d.date() ",[263,1541,1333],{"class":301},[263,1543,1544],{"class":305}," d ",[263,1546,1339],{"class":301},[263,1548,361],{"class":305},[263,1550,1551],{"class":273},"\"date\"",[263,1553,1554],{"class":305},"]]\n",[263,1556,1558,1560,1562,1564,1567,1569,1572,1574,1576,1578,1581,1583],{"class":265,"line":1557},15,[263,1559,417],{"class":392},[263,1561,420],{"class":305},[263,1563,423],{"class":392},[263,1565,1566],{"class":305},"(xnpv(",[263,1568,948],{"class":392},[263,1570,1571],{"class":305},", flows, dates), ",[263,1573,429],{"class":392},[263,1575,478],{"class":305},[263,1577,423],{"class":392},[263,1579,1580],{"class":305},"(xirr(flows, dates), ",[263,1582,1218],{"class":392},[263,1584,569],{"class":305},[10,1586,1587,1588,21,1591,1594,1595,1597,1598,1600,1601,1604,1605,591],{},"The results — 2,230.71 and 27.36% — match ",[14,1589,1590],{},"=XNPV(10%, A2:A5, B2:B5)",[14,1592,1593],{},"=XIRR(A2:A5, B2:B5)"," exactly. Note that ",[14,1596,28],{},", unlike ",[14,1599,20],{},", treats the first flow as happening today, so there is no leading-zero adjustment. Bisection is slower than the Newton method Excel uses but always converges when the range brackets a solution, which makes it the safer choice in a batch job; the ",[14,1602,1603],{},"ValueError"," reports the cases Excel would show as ",[14,1606,1607],{},"#NUM!",[249,1609,1611],{"id":1610},"step-6-write-results-with-the-formulas-alongside","Step 6 — Write results with the formulas alongside",[10,1613,1614],{},"For review, write both the Python figures and the equivalent Excel formulas, so a finance colleague can see that the two agree and the workbook keeps working if they change an input:",[254,1616,1618],{"className":292,"code":1617,"language":294,"meta":259,"style":259},"from openpyxl import load_workbook\n\nproject.to_excel(\"valuation.xlsx\", index=False)\nwb = load_workbook(\"valuation.xlsx\")\nws = wb.active\nrows = [(\"NPV at 10% (python)\", flows[0] + npf.npv(0.10, [0] + flows[1:])),\n        (\"NPV at 10% (formula)\", \"=B2+NPV(0.1,B3:B5)\"),\n        (\"XIRR (python)\", xirr(flows, dates)),\n        (\"XIRR (formula)\", \"=XIRR(B2:B5,A2:A5)\")]\nfor i, (label, value) in enumerate(rows, start=2):\n    ws.cell(row=i, column=4, value=label)\n    ws.cell(row=i, column=5, value=value)\nwb.save(\"valuation.xlsx\")\nprint([ws.cell(row=r, column=5).value for r in range(2, 6)])\n",[14,1619,1620,1633,1637,1655,1669,1679,1720,1736,1746,1761,1785,1815,1841,1850],{"__ignoreMap":259},[263,1621,1622,1625,1628,1630],{"class":265,"line":266},[263,1623,1624],{"class":301},"from",[263,1626,1627],{"class":305}," openpyxl ",[263,1629,302],{"class":301},[263,1631,1632],{"class":305}," load_workbook\n",[263,1634,1635],{"class":265,"line":315},[263,1636,332],{"emptyLinePlaceholder":331},[263,1638,1639,1642,1645,1647,1649,1651,1653],{"class":265,"line":328},[263,1640,1641],{"class":305},"project.to_excel(",[263,1643,1644],{"class":273},"\"valuation.xlsx\"",[263,1646,402],{"class":305},[263,1648,812],{"class":811},[263,1650,341],{"class":301},[263,1652,817],{"class":392},[263,1654,350],{"class":305},[263,1656,1657,1660,1662,1665,1667],{"class":265,"line":335},[263,1658,1659],{"class":305},"wb ",[263,1661,341],{"class":301},[263,1663,1664],{"class":305}," load_workbook(",[263,1666,1644],{"class":273},[263,1668,350],{"class":305},[263,1670,1671,1674,1676],{"class":265,"line":353},[263,1672,1673],{"class":305},"ws ",[263,1675,341],{"class":301},[263,1677,1678],{"class":305}," wb.active\n",[263,1680,1681,1684,1686,1689,1692,1695,1697,1699,1701,1703,1705,1707,1709,1711,1713,1715,1717],{"class":265,"line":528},[263,1682,1683],{"class":305},"rows ",[263,1685,341],{"class":301},[263,1687,1688],{"class":305}," [(",[263,1690,1691],{"class":273},"\"NPV at 10% (python)\"",[263,1693,1694],{"class":305},", flows[",[263,1696,66],{"class":392},[263,1698,708],{"class":305},[263,1700,981],{"class":301},[263,1702,1012],{"class":305},[263,1704,948],{"class":392},[263,1706,974],{"class":305},[263,1708,66],{"class":392},[263,1710,708],{"class":305},[263,1712,981],{"class":301},[263,1714,1025],{"class":305},[263,1716,519],{"class":392},[263,1718,1719],{"class":305},":])),\n",[263,1721,1722,1725,1728,1730,1733],{"class":265,"line":1400},[263,1723,1724],{"class":305},"        (",[263,1726,1727],{"class":273},"\"NPV at 10% (formula)\"",[263,1729,402],{"class":305},[263,1731,1732],{"class":273},"\"=B2+NPV(0.1,B3:B5)\"",[263,1734,1735],{"class":305},"),\n",[263,1737,1738,1740,1743],{"class":265,"line":1423},[263,1739,1724],{"class":305},[263,1741,1742],{"class":273},"\"XIRR (python)\"",[263,1744,1745],{"class":305},", xirr(flows, dates)),\n",[263,1747,1748,1750,1753,1755,1758],{"class":265,"line":1439},[263,1749,1724],{"class":305},[263,1751,1752],{"class":273},"\"XIRR (formula)\"",[263,1754,402],{"class":305},[263,1756,1757],{"class":273},"\"=XIRR(B2:B5,A2:A5)\"",[263,1759,1760],{"class":305},")]\n",[263,1762,1763,1765,1768,1770,1773,1776,1779,1781,1783],{"class":265,"line":1458},[263,1764,1333],{"class":301},[263,1766,1767],{"class":305}," i, (label, value) ",[263,1769,1339],{"class":301},[263,1771,1772],{"class":392}," enumerate",[263,1774,1775],{"class":305},"(rows, ",[263,1777,1778],{"class":811},"start",[263,1780,341],{"class":301},[263,1782,429],{"class":392},[263,1784,1384],{"class":305},[263,1786,1787,1790,1793,1795,1798,1801,1803,1805,1807,1810,1812],{"class":265,"line":1479},[263,1788,1789],{"class":305},"    ws.cell(",[263,1791,1792],{"class":811},"row",[263,1794,341],{"class":301},[263,1796,1797],{"class":305},"i, ",[263,1799,1800],{"class":811},"column",[263,1802,341],{"class":301},[263,1804,65],{"class":392},[263,1806,402],{"class":305},[263,1808,1809],{"class":811},"value",[263,1811,341],{"class":301},[263,1813,1814],{"class":305},"label)\n",[263,1816,1817,1819,1821,1823,1825,1827,1829,1832,1834,1836,1838],{"class":265,"line":1511},[263,1818,1789],{"class":305},[263,1820,1792],{"class":811},[263,1822,341],{"class":301},[263,1824,1797],{"class":305},[263,1826,1800],{"class":811},[263,1828,341],{"class":301},[263,1830,1831],{"class":392},"5",[263,1833,402],{"class":305},[263,1835,1809],{"class":811},[263,1837,341],{"class":301},[263,1839,1840],{"class":305},"value)\n",[263,1842,1843,1846,1848],{"class":265,"line":1526},[263,1844,1845],{"class":305},"wb.save(",[263,1847,1644],{"class":273},[263,1849,350],{"class":305},[263,1851,1852,1854,1857,1859,1861,1864,1866,1868,1870,1873,1875,1878,1880,1883,1885,1887,1889,1891],{"class":265,"line":1531},[263,1853,417],{"class":392},[263,1855,1856],{"class":305},"([ws.cell(",[263,1858,1792],{"class":811},[263,1860,341],{"class":301},[263,1862,1863],{"class":305},"r, ",[263,1865,1800],{"class":811},[263,1867,341],{"class":301},[263,1869,1831],{"class":392},[263,1871,1872],{"class":305},").value ",[263,1874,1333],{"class":301},[263,1876,1877],{"class":305}," r ",[263,1879,1339],{"class":301},[263,1881,1882],{"class":392}," range",[263,1884,420],{"class":305},[263,1886,429],{"class":392},[263,1888,402],{"class":305},[263,1890,1218],{"class":392},[263,1892,1893],{"class":305},")])\n",[10,1895,1896],{},"Keeping one column of Python values and one of live formulas makes discrepancies obvious at a glance, which is the fastest way to win trust for a calculation that moved out of a spreadsheet.",[249,1898,1900],{"id":1899},"common-pitfalls","Common pitfalls",[1902,1903,1904,1918,1924,1930,1941],"ul",{},[1905,1906,1907,1911,1912,1914,1915,1917],"li",{},[1908,1909,1910],"strong",{},"Excel NPV versus npf.npv."," Prepend ",[14,1913,66],{}," to match ",[14,1916,20],{}," over all flows; use flows as they are for a proper project value.",[1905,1919,1920,1923],{},[1908,1921,1922],{},"Mixing annual rates with monthly periods."," Divide the rate by 12 and multiply the years by 12 together.",[1905,1925,1926,1929],{},[1908,1927,1928],{},"Sign conventions."," Receipts positive, payments negative; flip signs only for display.",[1905,1931,1932,1940],{},[1908,1933,1934,1936,1937,591],{},[14,1935,583],{}," without ",[14,1938,1939],{},"fv"," It is required in Python; pass 0.",[1905,1942,1943,1946,1947,1950,1951,1954],{},[1908,1944,1945],{},"Payments at the start of the period."," Excel's optional ",[14,1948,1949],{},"type"," argument (1 for payments in advance) maps to ",[14,1952,1953],{},"when=\"begin\""," in numpy-financial; leave both at their defaults for ordinary loans, and set both when a model uses advance payments.",[249,1956,1958],{"id":1957},"performance-notes","Performance notes",[10,1960,1961,1962,1965,1966,1969,1970,1972],{},"numpy-financial's functions are vectorised over arrays, so payments for a million loans take a fraction of a second. The pure-Python ",[14,1963,1964],{},"xirr"," evaluates ",[14,1967,1968],{},"xnpv"," a few dozen times per project; for tens of thousands of projects, vectorise ",[14,1971,1968],{}," with NumPy arrays of day offsets, or run the projects in parallel.",[249,1974,1976],{"id":1975},"conclusion","Conclusion",[10,1978,1979,402,1982,402,1984,402,1987,21,1989,1991,1992,1994,1995,1998,1999,2001,2002,2004,2005,2008,2009,21,2011,2013,2014,21,2016,2018],{},[14,1980,1981],{},"npf.pmt",[14,1983,1939],{},[14,1985,1986],{},"nper",[14,1988,579],{},[14,1990,1240],{}," reproduce Excel's functions directly, with the same argument order and sign conventions (",[14,1993,579],{}," needs ",[14,1996,1997],{},"fv=0"," passed explicitly). ",[14,2000,1051],{}," differs from Excel's ",[14,2003,20],{},": Excel discounts the first value, so prepend a zero to match it — and value projects as ",[14,2006,2007],{},"initial + NPV(later flows)",". For dated flows, short ",[14,2010,1968],{},[14,2012,1964],{}," helpers match ",[14,2015,28],{},[14,2017,31],{}," exactly. Write the Python results next to live formulas when finance colleagues need to check them.",[249,2020,2022],{"id":2021},"frequently-asked-questions","Frequently asked questions",[10,2024,2025,2028],{},[1908,2026,2027],{},"Why is numpy-financial's npv different from Excel's NPV?","\nExcel's NPV treats the first value as arriving one period from now, so it discounts every value. numpy_financial.npv treats the first value as arriving today, undiscounted. To match Excel, prepend a zero, or add the initial investment separately: initial + NPV(rate, later flows).",[10,2030,2031,2034],{},[1908,2032,2033],{},"Why does PMT return a negative number?","\nBoth Excel and numpy-financial use a cash-flow sign convention: money you receive is positive and money you pay out is negative. Borrowing 200,000 (positive) means paying about 1,073.64 a month (negative).",[10,2036,2037,2040],{},[1908,2038,2039],{},"Does numpy-financial have XNPV and XIRR?","\nNo. Both are short to write: XNPV discounts each cash flow by (1 + rate) raised to the number of days since the first flow divided by 365, and XIRR is the rate that makes XNPV zero, found by bisection.",[10,2042,2043,2046],{},[1908,2044,2045],{},"Is numpy-financial still maintained?","\nIt is the package the financial functions moved to when they were removed from NumPy itself, and it is small and stable. Check the version you depend on and pin it, as with any dependency.",[249,2048,2050],{"id":2049},"related","Related",[1902,2052,2053,2059,2066,2073],{},[1905,2054,2055,2056,2058],{},"Up one level: ",[38,2057,41],{"href":40}," — every function, translated.",[1905,2060,2061,2065],{},[38,2062,2064],{"href":2063},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fmatch-excel-rounding-in-pandas\u002F","Match Excel Rounding in pandas"," — rounding payments the way the spreadsheet does.",[1905,2067,2068,2072],{},[38,2069,2071],{"href":2070},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-floating-point-noise-in-excel-output\u002F","Fix Floating-Point Noise Like 0.1 + 0.2 ≠ 0.3 in Excel Output"," — exactness in money calculations.",[1905,2074,2075,2079],{},[38,2076,2078],{"href":2077},"\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"," — cumulative cash-flow tables.",[2081,2082,2083],"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 .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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":259,"searchDepth":315,"depth":315,"links":2085},[2086,2087,2088,2089,2090,2091,2092,2093,2094,2095,2096,2097],{"id":251,"depth":315,"text":252},{"id":370,"depth":315,"text":371},{"id":594,"depth":315,"text":595},{"id":908,"depth":315,"text":909},{"id":1179,"depth":315,"text":1180},{"id":1248,"depth":315,"text":1249},{"id":1610,"depth":315,"text":1611},{"id":1899,"depth":315,"text":1900},{"id":1957,"depth":315,"text":1958},{"id":1975,"depth":315,"text":1976},{"id":2021,"depth":315,"text":2022},{"id":2049,"depth":315,"text":2050},"2026-09-25","Reproduce Excel's financial functions in Python with numpy-financial: loan payments with PMT, FV, NPER and RATE, project values with NPV and IRR, and dated cash flows with XNPV and XIRR. Every result here matches the spreadsheet — including the NPV difference that catches almost everyone.","md",[2102,2104,2106,2108],{"q":2027,"a":2103},"Excel's NPV treats the first value as arriving one period from now, so it discounts every value. numpy_financial.npv treats the first value as arriving today, undiscounted. To match Excel, prepend a zero, or add the initial investment separately: initial + NPV(rate, later flows).",{"q":2033,"a":2105},"Both Excel and numpy-financial use a cash-flow sign convention: money you receive is positive and money you pay out is negative. Borrowing 200,000 (positive) means paying about 1,073.64 a month (negative).",{"q":2039,"a":2107},"No. Both are short to write: XNPV discounts each cash flow by (1 + rate) raised to the number of days since the first flow divided by 365, and XIRR is the rate that makes XNPV zero, found by bisection.",{"q":2045,"a":2109},"It is the package the financial functions moved to when they were removed from NumPy itself, and it is small and stable. Check the version you depend on and pin it, as with any dependency.","pmt npv irr excel financial functions in python",null,{"breadcrumb":2113},[2114,2116,2119],{"name":2115,"item":724},"Home",{"name":2117,"item":2118},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":41,"item":40},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fpmt-npv-and-irr-in-python",{"title":2122,"description":2123},"Excel PMT, NPV, IRR, XNPV and XIRR in Python (numpy-financial)","Excel financial functions in Python: numpy_financial pmt, fv, nper, rate, npv and irr with sign conventions, why Excel NPV differs from npf.npv, and XNPV and XIRR for dated cash flows, all checked against spreadsheet results.","pmt-npv-and-irr-in-python","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fpmt-npv-and-irr-in-python\u002Findex",[2127,2128,2129],"formula-equivalents","pandas","reliable-jobs","how-to","DzQbcho7iaa0JtQxb1RYoM-VvSYXb7fRwtrIVyBxFtY",[2133,2137],{"title":2134,"path":2135,"stem":2136,"children":-1},"MEDIAN, MODE and QUARTILE Equivalents in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fmedian-mode-and-quartile-equivalents-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fmedian-mode-and-quartile-equivalents-in-pandas\u002Findex",{"title":2138,"path":2139,"stem":2140,"children":-1},"RANK and PERCENTILE Formulas in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Frank-and-percentile-formulas-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Frank-and-percentile-formulas-in-pandas\u002Findex",[2142,2144],{"path":2143,"title":41},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas",{"path":2145,"title":2117},"\u002Fadvanced-data-transformation-and-cleaning",[],[2148,2151,2154,2157,2160,2163],{"path":2149,"title":2150,"kind":2130},"\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":2152,"title":2153,"kind":2130},"\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":2155,"title":2156,"kind":2130},"\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":2158,"title":2159,"kind":2130},"\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":2161,"title":2162,"kind":2130},"\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":2164,"title":2165,"kind":2130},"\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",1790972088424]