DECCF · Cash Flow Modeler and Calculator

Valuation and uncertainty

Built-in metrics

  • total: the sum of net cash positions in flows and options over published result periods.
  • npv: present value at the run's annual discount rate.
  • irr: annualized internal rate of return when it can be solved.
  • wal: weighted average time to cashflow in years, when the data supports a value.

The discount rate is zero unless supplied; it must be finite and greater than −1. The model calendar sets periods per year: 365, 52, 12, 4, or 1. Schedule placement controls the time point within each period.

Declare custom metrics with functions in defineModel({ metrics: { name: (ctx) => ... } }). They run after the period loop with a context at the last calculated date. They can read inputs, fields, states, accounts, and completed cashflows. A callback can return undefined to omit a metric, which is useful when the model has no positive value to report. The names total, npv, irr, and wal are reserved.

ctx.sumAccountPostings(accounts, from?, to?, { excludePeriodClosing? }) aggregates signed debit/credit movements on selected accounts. This lets a metric select accounting activity through the account plan rather than flow names.

Distributions and draws

Distributions accept clip: [min, max], and can also be checked against input domains. Central values in a deterministic run are:

  • normal: mean
  • log-normal: expected value from mu and sigma
  • uniform: midpoint of the range
  • triangular: mode

To vary values, callbacks must use ctx.draw() or ctx.sample(). draw() reuses a value across periods; sample() draws once per period and owner. Correlations are not modeled automatically.

Monte Carlo

runMonteCarlo(model, { trials, seed, ...runOptions }) requires a positive integer trial count and an integer seed. It runs stochastic trials using pseudorandom samples initialized by the seed. The output summarizes sampled numeric metrics with valid sample count, mean, standard deviation, min, max, and percentiles p5, p25, p50, p75, and p95; it is not a deterministic forecast. It also returns runtime timings for each trial, the aggregate metric calculations, and the full simulation call. Timing values depend on runtime conditions and are not reproducible simulation outputs.

The engine prepares the model and calendar once per Monte Carlo call. It tracks context reads in calculations, derived inputs and flow amount callbacks, then reuses values that depend only on fixed run inputs, dates, or other reusable calculations. Reuse is per calculation or flow and period. ctx.get() on a distribution reads its center; ctx.draw() and ctx.sample() prevent exact reuse unless that input has a fixed run override. Reads of mutable accounts, fields, cashflows, states, trigger sources, or posting history conservatively prevent reuse. Each trial still has its own balances, postings, events, and random draws.

Monte Carlo trials build only final metric values and timings. They retain the simulation history needed by modules and evaluate every scheduled metric, including earlier periods and validation. They skip published result series, annual rollups, report groups, input descriptions, graph metadata, and copies of ledger rows. An ordinary run() continues to return the complete result.

Caches stay within the call, so changed inputs, scenarios, currencies, and model versions prepare separate results. SIE statements also keep their last successfully parsed file by exact content, while account mappings are rebuilt for each compilation. Callbacks must be pure: use the context for randomness and model data, rather than ambient randomness, clocks, I/O, or mutable captured state. The engine cannot detect those external dependencies.

Select sampling: "sobol" to use a randomized Sobol sequence instead of the default "pseudo-random" sampler:

Sobol requires a power-of-two trial count. It uses the Joe–Kuo direction numbers with a seeded lower-triangular matrix scramble and digital shift (LMS+shift). Every distribution, owner, period and uniform coordinate receives a stable slot before execution; normal and log-normal draws retain their two-coordinate Box–Muller transform. Conditional calls do not shift other inputs' streams. Slots are reserved even for overridden inputs, and the prepared plan supports at most 21,201 coordinates. Structural scenario changes can change the slot layout; the same seed does not guarantee paired streams across different structures. Individual simulations still preserve all their history. Sobol can improve estimation accuracy with fewer simulations, but does not guarantee a faster run or improvement for every model. The reported standard deviation describes the distribution of outcomes, not uncertainty in the mean estimate. Use independently seeded replicate batches to assess numerical estimation error.

Approximate Monte Carlo

Set approximate: true to permit approximation. It activates only above 1,000 trials; smaller runs still use exact draws and report approximation.active: false. Defaults remain exact. The Calculator and Web API support up to 4,096 trials per scenario, including balanced Sobol sizes 1,024, 2,048 and 4,096.

Approximation divides each uniform random coordinate into midpoint bins before transforming it to the chosen distribution. approximationBins accepts 16–4,096, with default 64. This changes the sampled distribution, including its tails; more bins give finer resolution. Flow amount callbacks with one or two directly sampled numeric dependencies can reuse observed grid values in a bounded table of up to 256 points per flow and period. Conditional branches keep their original read order. Callbacks that read history, mutable state, trigger sources or dependent calculations remain fully evaluated. There is no interpolation across unobserved cells.

When at least 20 distinct, actually evaluated grid points return the same amount, a histogram shortcut may reuse that amount for new points. Every eighth shortcut attempt reevaluates the callback. Observed variation or newly discovered unsafe dependencies permanently disables this shortcut for the affected flow and period. Earlier skipped rare outcomes cannot be recovered automatically; the result always includes a caution when histogram shortcuts were used.

Observed one- and two-input response curves are checked for sharp jumps or changes of slope. Warnings name the affected flow and period; sparse or capped observations are marked insufficient. This is a diagnostic, not an accuracy guarantee: an unseen threshold can still be missed. Run the exact mode to check important estimates, quantiles and rare events. Approximation counters, warnings and assessment status are returned under result.approximation and shown beside the Calculator's summaries.

Numeric metric columns and sorting scratch are private to each executor and reused across metric summaries. Full simulation histories remain separate per trial, including histories reachable through retained callback contexts. Repeated growing cashflow and posting sums extend an ordered fold instead of restarting at the beginning; they retain the original floating-point addition order and read-permission checks.

Scenarios

runScenarios(model, [{ name, overrides }], options) evaluates every named scenario and returns an array of { name, result }, plus batch timings on the array. Each overrides value is a Map keyed by declared input references; scalar inputs use their usual values, while curve inputs use { series: { points, interpolation } }. Put scenario-specific model inputs in each scenario's map. Shared run options such as currency and discount rate apply to every scenario. Pass trials and seed to run Monte Carlo trials for every scenario and receive a summary result per scenario. Object-based deterministic.inputs is available to ordinary run() calls, but is not the scenario override format.

The Calculator's Scenarios tab calls this batch API when you select Run model. It submits all cards together, merging each card's selected values over the current Model inputs, then displays the returned scenario results through report tabs. See Create scenarios in the Calculator.