Financial functions

ACCRINT

Matches all 146,695 recorded answers

Recorded: 146,688 in the answer bank and 7 workbook cells. How we measure this · See the Rust code

Local names in 40 languages
LanguageName in Excel
ArabicACCRINT
BulgarianACCRINT
CzechACCRINT
DanishACCRINT
GermanACCRINT
GreekACCRINT
English (Australia)ACCRINT
English (United Kingdom)ACCRINT
English (United States)ACCRINT
SpanishINT.ACUM
EstonianACCRINT
FinnishKERTYNYT.KORKO
FrenchINTERET.ACC
HebrewACCRINT
CroatianACCRINT
HungarianIDŐSZAKI.KAMAT
IndonesianACCRINT
ItalianACCRINT
JapaneseACCRINT
KoreanACCRINT
LithuanianACCRINT
LatvianACCRINT
NorwegianACCRINT
DutchSAMENG.RENTE
PolishNAL.ODS
Portuguese (Brazil)ACCRINT
Portuguese (Portugal)JUROSACUM
RomanianACCRINT
RussianНАКОПДОХОД
SlovakACCRINT
SlovenianACCRINT
Serbian (Latin)ACCRINT
SwedishUPPLRÄNTA
ThaiACCRINT
TurkishGERÇEKFAİZ işlevi
UkrainianACCRINT
VietnameseACCRINT
Chinese (Simplified)ACCRINT
Chinese (Hong Kong)ACCRINT
Chinese (Traditional)ACCRINT

Names from Microsoft's support site.

What it does

ACCRINT(issue, first_interest, settlement, rate, par, frequency, [basis], [calc_method])

ACCRINT returns the accrued interest on a security that pays periodic coupons — the interest that has built up between the security's issue date and its settlement date, and that a buyer therefore owes the seller on top of the price.

The arithmetic is a rate times a principal times a length of time:

accrued = par × rate × (accrued time, measured in years on the chosen basis)

Written the way coupon markets write it, the year is divided into frequency coupon periods and the elapsed time is counted in periods rather than in years:

accrued = par × (rate / frequency) × Σ (A_i / NL_i)

where the sum runs over the quasi-coupon periods touched by the accrual interval, A_i is the number of accrued days inside period i counted on the basis, and NL_i is the normal length of period i on the same basis. A period that is entirely inside the interval contributes exactly 1; the two end periods contribute fractions.

That decomposition is what makes ACCRINT different from its at-maturity sibling ACCRINTM, which needs no schedule at all: ACCRINTM measures one interval against one year, while ACCRINT has to reconstruct the coupon schedule backwards from first_interest, decide which periods the interval crosses, and normalize each piece by that period's own length. Every hard question about this function is a question about that reconstruction.

Microsoft's worksheet-function page publishes an equation of the shape above. That page was not retrieved for this entry — the support host returned HTTP 403 — so the formula printed here is stated as the standard accrual identity in the Handbook's own voice, not quoted from Microsoft. The parameter list, the required/optional split and the basis table below are taken from Microsoft's Learn reference for the same function.

Two lengths, and which one normalizes

The subtle part is NL_i. For a whole interior period there is nothing to decide. For the period that contains settlement, and for the stub period that contains issue, the implementation has to choose between:

  1. the actual length of that particular period (which under actual/actual differs from period to period, because half-years are 181, 182, 183 or 184 days long), and
  2. a canonical length — the length of the last coupon period before first_interest, used for every fractional piece regardless of where it sits.

Arguments

ACCRINT(issue, first_interest, settlement, rate, par, frequency, [basis], [calc_method])

ArgumentMeaningRequired?
issueThe security's issue date — where accrual starts.Required
first_interestThe security's first interest (first coupon) date.Required
settlementThe date the trade settles — where accrual stops.Required
rateThe security's annual coupon rate.Required
parThe security's par value.Required in the documented list
frequencyCoupon payments per year: 1, 2 or 4.Required
basisDay-count convention, 0–4.Optional, defaults to 0
calc_methodWhich accrual construction to use.Optional, defaults to TRUE

rate is annual, frequency divides it. The single most common modelling error in this family is feeding a per-period rate into an argument that Excel will divide by frequency again.

basis selects the day-count convention — 0 or omitted = US (NASD) 30/360, 1 = actual/actual, 2 = actual/360, 3 = actual/365, 4 = European 30/360 — exactly the table on Microsoft's Learn page for this function, and the same axis described at length on YEARFRAC. A workbook that omits basis is asking for 30/360, whatever the instrument's term sheet says.

The date arguments are numeric slots holding Excel date serials, subject to ordinary to-number coercion; see Coercion and lifting. Microsoft's page carries the standard warning that dates should be produced by DATE or by other formulas rather than typed as text.

Try it

Input cells (optional)

Example: {"B2": 3, "B3": 4}. A1 holds the formula. Other cells need values before a formula can read them.

Enter a formula, then calculate.

Runs locally in your browser with OneCalc. Its answer may differ from Excel. Use English function names and commas between arguments.

Quirks

The calc_method argument

calc_method is documented — on the worksheet-function page, not on the Learn page consulted here — as a choice of starting line: TRUE accrues from issue, FALSE accrues from first_interest.

Two consequences follow, and both are visible without any last-bit machinery:

  1. Accrual is not monotone in issue date under FALSE. A bond issued three periods before first_interest can accrue less than one issued a single period before, because the intervening whole periods vanish.

Result and edge cases

Returns a Number: an amount of currency in the same units as par, not a rate and not a percentage.

  • Settlement exactly on a coupon date is the interesting boundary. Under the reference engine's TRUE path the final period is always measured as days divided by the canonical length, so it stays a fraction rather than snapping to a whole period.
  • Month-end and leap-day dates are where the schedule reconstruction earns or loses its accuracy: the backward walk from first_interest preserves month-end anchoring, so a coupon dated the 30th of a 30-day month and one dated the 31st generate different schedules.
  • Fractional arguments. Date and basis arguments are truncated toward zero before use.
  • Empty, missing and error arguments follow the shared call model; see The value universe and Coercion and lifting.

Errors

Microsoft's Learn reference for this function documents no error conditions at all — it carries only the parameter table and the basis table. What follows is therefore the reference engine's behaviour, recorded as such, plus the conditions Microsoft documents for the sibling functions in this family:

How to implement

Numerical notes

The difficulty here is not floating point in the usual sense — there is no cancellation catastrophe and no argument reduction. It is combinatorial: getting the same sequence of small quotients that Excel forms, and then adding them in the same order.

  1. Schedule generation is the algorithm. Walking back from first_interest by 12/frequency months, with month-end anchoring, is the whole of the hard part. Two defensible month-arithmetic rules (clamp the day of month, versus remember that the anchor was a month end) produce schedules that differ by a day several times a year.
  2. Two 30/360 implementations will eventually disagree. The Handbook's own research record documents a case where two routines implementing "the same" US 30/360 rule differed only in which adjustment they applied first, and disagreed by one day on month-end pairs. Share one day-count routine; do not re-derive it per family.
  3. The canonical-versus-actual period length choice (above) is a semantic choice with numerical consequences far larger than one ULP. It belongs in the specification, not in the rounding discussion.
  4. There is no known closed form that avoids the schedule for a general issue; attempts to shortcut it with a single YEARFRAC are correct only within one period.

Rust code

Show the CalcFunc source of this function

Quoted from the public repository DnaCalc/DnaCalc, crates calc-func and calc-fml, at commit 9e1884369a67. Comments are the source's own. A kernel shared with other functions appears once, on its module page.

ACCRINT · 6 lines · calc-func/src/catalogue/bonds.rs on GitHub
8 accrint_fn "ACCRINT" (6, 8) [V "issue", V "first_interest", V "settlement", V "rate", V "par", V "frequency", V "basis", V "calc_method"] |s, a, sv| {
    let (issue, first, settlement) = (date_1900(s, a, 0, sv)?, date_1900(s, a, 1, sv)?, date_1900(s, a, 2, sv)?);
    let par = or(s, a, 4, 1000.0)?;
    let calc_method = if given(a, 7) { num(s, a, 7)?.trunc() != 0.0 } else { true };
    bond_out(s, bond::accrint_kernel(issue, first, settlement, num(s, a, 3)?, Some(par), num(s, a, 5)?, Some(or(s, a, 6, 0.0)?), Some(calc_method)))
}
is_month_end · 5 lines · calc-func/src/ported/bond_core_family.rs on GitHub
fn is_month_end(s: i64) -> bool {
    ymd_from_excel_serial(WorkbookDateSystem::System1900, s as f64)
        .map(|(y, mo, d)| d == dim(y, mo))
        .unwrap_or(false)
}
change_month_flag · 17 lines · calc-func/src/ported/bond_core_family.rs on GitHub
/// F# `changeMonth` with an explicit return-last-day flag (.NET `AddMonths`
/// semantics, or force the target month-end when `last_day` is set).
fn change_month_flag(s: i64, months: i64, last_day: bool) -> Option<i64> {
    let (y, mo, d) = ymd_from_excel_serial(WorkbookDateSystem::System1900, s as f64)?;
    let idx = y
        .checked_mul(12)?
        .checked_add(mo - 1)?
        .checked_add(months)?;
    let ty = idx.div_euclid(12);
    let tm = idx.rem_euclid(12) + 1;
    let td = if last_day {
        dim(ty, tm)
    } else {
        d.min(dim(ty, tm))
    };
    excel_serial_from_ymd(WorkbookDateSystem::System1900, ty, tm, td).map(|v| v as i64)
}
find_pcd_ncd_accr · 24 lines · calc-func/src/ported/bond_core_family.rs on GitHub
/// F# `findPcdNcd`: walk `start` toward `end` by `num_months` steps, returning the
/// bounding pair (the position at/past `end`, and the one before it).
fn find_pcd_ncd_accr(
    start: i64,
    end: i64,
    num_months: i64,
    last_day: bool,
) -> Result<(i64, i64), BondCoreEvalError> {
    let mut front = start;
    let mut trailing = end;
    loop {
        let stop = if num_months > 0 {
            front >= end
        } else {
            front <= end
        };
        if stop {
            return Ok((front, trailing));
        }
        trailing = front;
        front =
            change_month_flag(front, num_months, last_day).ok_or(derr(WorksheetErrorCode::Num))?;
    }
}
days_between_denum · 12 lines · calc-func/src/ported/bond_core_family.rs on GitHub
/// F# `IDayCount.DaysBetween` (denominator position) per basis — only the
/// ActualActual / Actual360 / European cases are reached (the kernel handles
/// Us30_360 and Actual365 inline).
fn days_between_denum(s: i64, e: i64, b: DayCountBasis) -> Result<f64, BondCoreEvalError> {
    match b {
        DayCountBasis::ActualActual => Ok(act(s, e)),
        DayCountBasis::Actual360 => diff360_us(s, e, false),
        DayCountBasis::European30_360 => d360eu(s, e),
        DayCountBasis::Us30_360 => diff360_us(s, e, false),
        DayCountBasis::Actual365 => Ok(act(s, e)),
    }
}
actual_coup_days_accr · 13 lines · calc-func/src/ported/bond_core_family.rs on GitHub
/// F# `actualCoupDays pcd firstInterest`: the actual length of the coupon period
/// (on `first`'s schedule) that bounds `pcd`. Works in both regimes — `pcd` a
/// period before `first` (odd first coupon) or after it (settlement past the first
/// interest date), where the bounding period differs.
fn actual_coup_days_accr(
    pcd: i64,
    first: i64,
    num_months: i64,
    last_day: bool,
) -> Result<f64, BondCoreEvalError> {
    let (prev, next) = find_pcd_ncd_accr(first, pcd, -num_months, last_day)?;
    Ok(act(prev, next))
}
coup_days_accr · 17 lines · calc-func/src/ported/bond_core_family.rs on GitHub
/// F# `IDayCount.CoupDays pcd firstInterest`: the normal length of the coupon
/// period anchored at `pcd`. ActualActual uses the actual bounding-period days; the
/// 30/360 bases use `360/freq`, Actual/365 uses `365/freq`.
fn coup_days_accr(
    pcd: i64,
    first: i64,
    num_months: i64,
    last_day: bool,
    fc: f64,
    b: DayCountBasis,
) -> Result<f64, BondCoreEvalError> {
    match b {
        DayCountBasis::ActualActual => actual_coup_days_accr(pcd, first, num_months, last_day),
        DayCountBasis::Actual365 => Ok(365.0 / fc),
        _ => Ok(360.0 / fc),
    }
}
issue_period_grid · 19 lines · calc-func/src/ported/bond_core_family.rs on GitHub
/// The coupon period `[B0, B1]` on `first`'s grid schedule that contains `issue`
/// (`B0 <= issue < B1`). Uses a STRICT lower step so an `issue` landing exactly on a grid
/// boundary yields `B1 == issue` (a zero-length stub — the whole periods are then skipped).
fn issue_period_grid(
    issue: i64,
    first: i64,
    num_months: i64,
    last_day: bool,
) -> Result<(i64, i64), BondCoreEvalError> {
    let mut b1 = first;
    while b1 > issue {
        b1 = change_month_flag(b1, -num_months, last_day).ok_or(derr(WorksheetErrorCode::Num))?;
    }
    while b1 < issue {
        b1 = change_month_flag(b1, num_months, last_day).ok_or(derr(WorksheetErrorCode::Num))?;
    }
    let b0 = change_month_flag(b1, -num_months, last_day).ok_or(derr(WorksheetErrorCode::Num))?;
    Ok((b0, b1))
}
accrint_kernel · 143 lines · calc-func/src/ported/bond_core_family.rs on GitHub
pub fn accrint_kernel(
    issue: f64,
    first_interest: f64,
    settlement: f64,
    rate_: f64,
    par: Option<f64>,
    frequency: f64,
    basis_: Option<f64>,
    calc_method: Option<bool>,
) -> Result<f64, BondCoreEvalError> {
    let issue = dser(issue)?;
    let first = dser(first_interest)?;
    let settlement = dser(settlement)?;
    let rate_ = rate(rate_)?;
    let par = pos(par.unwrap_or(1000.0))?;
    let f = freq(frequency)?;
    let basis_ = basis(basis_.unwrap_or(0.0))?;
    // calc_method: TRUE = FromIssueToSettlement (Excel default), FALSE = FromFirstToSettlement.
    let from_issue = calc_method.unwrap_or(true);
    if !(issue < first && issue < settlement) {
        return Err(derr(WorksheetErrorCode::Num));
    }
    // Identified against 145,620 live-Excel witnesses (W109 G6-02, agent-W; b39/b40/b42),
    // 99.99% bit-exact — plain SSE2 `f64` throughout (x87 emulation is strictly worse).
    // Excel normalises the settlement-side fraction by the *canonical* last coupon period
    // length `CoupDays(first - 1 period, first)` (fixes the ~0.07% act/act error of the prior
    // kernel — BUG-FUNC-030). Two calc_method paths, both accruing from `issue`:
    //  * calc_method TRUE  (from_issue) => period-aware WALK: interior whole periods = 1.0,
    //    the act/act issue stub uses its own actual length, and the settlement-side period is
    //    always days/canonical (stays fractional even when settle is on a coupon date). The
    //    forward-collected terms are summed BACKWARD (settlement -> issue).
    //  * calc_method FALSE            => FLAT days(issue->settle)/canonical, EXCEPT when issue
    //    sits in a coupon period earlier than `pcd`: the accrual then counts only the stub in
    //    issue's own grid period plus the span from `pcd` to settle, and SKIPS every whole
    //    coupon period between them (the defining legacy calc_method=FALSE behaviour).
    let fc = f as f64;
    let num_months = 12 / f;
    let end_flag = is_month_end(first);
    let pcd =
        change_month_flag(first, -num_months, end_flag).ok_or(derr(WorksheetErrorCode::Num))?;
    let canonical = coup_days_accr(pcd, first, num_months, end_flag, fc, basis_)?;
    // W109 G6-02 publication identification (build 20228/CV2 NoCache): the
    // coupon and accrual fraction are both stored binary64 values, but their
    // final product is an x87 PC64 multiply followed by the binary64 result
    // store. This rare double-rounding site is decisive on the b43 rate ladder
    // and the frozen publication held-out; all earlier day-count arithmetic
    // remains the identified plain-SSE2 graph.
    let coupon = par * rate_ / fc;

    if !from_issue {
        // ---- calc_method FALSE: FLAT fraction (with the whole-period skip). ----
        let a = if issue < pcd {
            // issue in a coupon period earlier than the canonical (last-before-first) period.
            // Accrue the stub in issue's own grid period [B0,B1]; SKIP the whole grid periods
            // [B1, pcd] entirely; measure the remainder from pcd. Two divisions, summed.
            let (b0, b1) = issue_period_grid(issue, first, num_months, end_flag)?;
            let l_issue = match basis_ {
                DayCountBasis::ActualActual => act(b0, b1),
                DayCountBasis::Actual365 => 365.0 / fc,
                _ => 360.0 / fc,
            };
            let stub = days_between_num(issue, b1, basis_)? / l_issue;
            let rest = days_between_num(pcd, settlement, basis_)? / canonical;
            stub + rest
        } else {
            days_between_num(issue, settlement, basis_)? / canonical
        };
        return Ok(crate::ported::excel_numeric::excel_x87_mul(coupon, a));
    }

    // ---- calc_method TRUE: period walk accruing from issue. ----
    if settlement <= first {
        let first_date = issue.max(pcd);
        let days = days_between_num(first_date, settlement, basis_)?;
        let mut a = days / canonical;
        // datesAggregate1: walk pcd back toward issue, one quasi-coupon period per step.
        let mut front = pcd;
        while front > issue {
            let ncd_i = front;
            let pcd_i = change_month_flag(front, -num_months, end_flag)
                .ok_or(derr(WorksheetErrorCode::Num))?;
            front = pcd_i;
            if issue <= pcd_i {
                a += 1.0;
            } else {
                // The period that contains `issue`: a partial forward fraction.
                let fd = issue.max(pcd_i);
                let days_i = match basis_ {
                    DayCountBasis::Us30_360 => diff360_us(fd, ncd_i, false)?,
                    _ => days_between_num(fd, ncd_i, basis_)?,
                };
                let coup_days_i = match basis_ {
                    DayCountBasis::Us30_360 => diff360_us(pcd_i, ncd_i, true)?,
                    DayCountBasis::ActualActual => act(pcd_i, ncd_i),
                    DayCountBasis::Actual365 => 365.0 / fc,
                    _ => days_between_denum(pcd_i, ncd_i, basis_)?,
                };
                a += days_i / coup_days_i;
            }
        }
        return Ok(crate::ported::excel_numeric::excel_x87_mul(coupon, a));
    }

    // Settlement past the first interest date: forward-collect the period terms, then sum
    // them BACKWARD (settlement side first — the direction Excel accumulates). Interior
    // COMPLETE periods contribute exactly 1.0; the act/act issue stub uses its own actual
    // length; the FINAL period touching settlement is always days/canonical (it stays
    // fractional even when settlement lands exactly on the period-end coupon date).
    let mut p_start = first;
    while p_start > issue {
        p_start = change_month_flag(p_start, -num_months, end_flag)
            .ok_or(derr(WorksheetErrorCode::Num))?;
    }
    let mut terms: Vec<f64> = Vec::new();
    while p_start < settlement {
        let p_end = change_month_flag(p_start, num_months, end_flag)
            .ok_or(derr(WorksheetErrorCode::Num))?;
        let ov_start = issue.max(p_start);
        let ov_end = settlement.min(p_end);
        if ov_end > ov_start {
            let is_last = ov_end == settlement;
            if ov_start == p_start && ov_end == p_end && !is_last {
                terms.push(1.0);
            } else {
                let denom = if matches!(basis_, DayCountBasis::ActualActual)
                    && ov_start == issue
                    && issue > p_start
                {
                    act(p_start, p_end)
                } else {
                    canonical
                };
                terms.push(days_between_num(ov_start, ov_end, basis_)? / denom);
            }
        }
        p_start = p_end;
    }
    let mut a = 0.0;
    for t in terms.iter().rev() {
        a += *t;
    }
    Ok(crate::ported::excel_numeric::excel_x87_mul(coupon, a))
}

Kernels shared with other functions

Argument and result plumbing

No implementation has been published with a Handbook test suite for this function.

Version differences

The recorded environments below distinguish a reading from a same-input comparison. Counts describe captured inputs in each environment; overlapping captures and comparisons are not totals of unique worksheet calls.

Excel 16.0 buildPlatform and CPUCaptured inputs
20131Windows, bitness not recorded, AMD Ryzen 7 4800H with Radeon Graphics145,458
20228Windows, 64-bit, CPU not recorded1,230
  • XC-ACCRINT-001: build 20131 versus build 20228; 780 same-input pairs, 0 differing answers. Compared axes: excel-build. Numeric answers were compared by their recorded binary64 bits; other answers by typed value.

Agreement on these compared inputs does not establish agreement on unmeasured inputs.

Recorded environments and comparison details.

Excel 16.0 build 20131

Windows, bitness not recorded, AMD Ryzen 7 4800H with Radeon Graphics

145,458 captured inputs are indexed for this environment. Capture dates: 2026-07-19 to 2026-07-20. This is a reading count, not a comparison with another build.

Excel 16.0 build 20228

Windows, 64-bit, CPU not recorded

1,230 captured inputs are indexed for this environment. Capture dates: 2026-08-09 to 2026-08-09. This is a reading count, not a comparison with another build.

Alternatives

Relationships

  • ACCRINTM — the same question for a security that pays all its interest at maturity. No coupon schedule, no frequency, no calc_method; one interval over one year fraction. If you are unsure which you need, the test is whether the instrument pays coupons before maturity.
  • The coupon-date functions — COUPPCD, COUPNCD, COUPDAYBS, COUPDAYS — expose the schedule that ACCRINT reconstructs internally. They are the right instruments for auditing an ACCRINT result: if COUPDAYBS/COUPDAYS disagrees with the fraction implied by ACCRINT, the disagreement is localized to one of the two.
  • YEARFRAC — the standalone day-count engine. ACCRINT on a single whole period is a YEARFRAC computation wearing a coupon schedule.
  • Confused with: ACCRINTM (above), and with simple rate × par × YEARFRAC(...) hand arithmetic, which reproduces ACCRINT only when the interval lies inside one coupon period.

Named variants

No named variants are recorded yet.

Suggest a correction or contribute an implementation · For agents