Choose a timeline and schedules
The timeline defines the periods the engine calculates. Calendars can be daily, weekly, monthly, quarterly, or annual.
import { cashflow, currencies, defineModel, entity, inflow, periods, schedule } from "deccf";
const timeline = periods.monthly({
from: "2026-01",
count: 24,
projection: 3,
});
const building = entity.asset();
const rent = cashflow({ owner: building, direction: inflow, schedule: schedule.monthly(), amount: 1_000 });
const model = defineModel({ name: "projected-rent", currency: currencies.SEK, timeline, entities: { building }, flows: { rent } });
return model;count is the published result horizon. projection adds calculation periods after it. The engine evaluates those extra periods, but the current result limits public dates, series, and built-in metrics to count periods. Custom metrics are evaluated at the last calculated date.
Schedules
import { cashflow, currencies, defineModel, entity, inflow, periods, schedule } from "deccf";
const building = entity.asset();
const once = cashflow({ owner: building, direction: inflow, schedule: schedule.once("2026-01-15"), amount: 100 });
const monthly = cashflow({ owner: building, direction: inflow, schedule: schedule.monthly({ from: "2026-01", to: "2027-12" }), amount: 200 });
const everyOtherQuarter = cashflow({ owner: building, direction: inflow, schedule: schedule.quarterly({ interval: 2 }), amount: 300 });
const yearEnd = cashflow({ owner: building, direction: inflow, schedule: schedule.yearEnd({ from: "2026-01", to: "2028-12" }), amount: 400 });
const midMonth = cashflow({ owner: building, direction: inflow, schedule: schedule.monthly({ dayOfMonth: 15 }), amount: 500 });
const monthEnd = cashflow({ owner: building, direction: inflow, schedule: schedule.monthly({ endOfMonth: true, except: ["2026-12-31"] }), amount: 600 });
const extraDate = cashflow({ owner: building, direction: inflow, schedule: schedule.monthly({ also: ["2026-04-05"] }), amount: 700 });
const model = defineModel({
name: "schedule-examples",
currency: currencies.SEK,
timeline: periods.monthly({ from: "2026-01", count: 36 }),
entities: { building },
flows: { once, monthly, everyOtherQuarter, yearEnd, midMonth, monthEnd, extraDate },
});
return model;Factories include once, daily, weekly, monthly, quarterly, annual, yearEnd, and every(frequency, options). Recurring schedules accept inclusive from, optional to, a positive interval, except, and also. yearEnd places one occurrence on December 31 of each represented calendar year. Monthly schedules can use either dayOfMonth or endOfMonth; a requested day is clamped to that month's last day when needed.
An occurrence maps to a model period. The engine rejects a schedule finer than the timeline can represent: a monthly model cannot preserve separate daily occurrences. Amount functions still run at most once per scheduled period.
Placement within a period
placement can be "start", "mid", or "end". A one-time schedule with an exact date uses the date's position within the period when no placement is set. Placement affects present-value timing and weighted average life, not how often the amount function runs.
Phases
import { cashflow, currencies, defineModel, entity, inflow, periods, phase, schedule } from "deccf";
const building = entity.asset();
const construction = phase({ from: "2026-01", to: "2026-06" });
const operations = phase({ from: "2026-07", to: "2027-12" });
const rent = cashflow({ owner: building, direction: inflow, schedule: schedule.monthly(), amount: 1_000 });
const model = defineModel({
name: "phased-building",
currency: currencies.SEK,
timeline: periods.monthly({ from: "2026-01", count: 24 }),
entities: { building },
flows: { rent },
phases: { construction, operations },
});
return model;Phases cannot overlap and must fit inside the timeline. In callbacks, ctx.time.phase contains the active phase name and ctx.inPhase(operations) checks a phase reference. schedule.phaseStart, phaseEnter, and phaseEnd place an occurrence at the phase start, entry, or end.