๐
Scheduled Investment Provider (scheduled_investment)
A synthetic, deterministic provider that calculates the value of an asset based on a predefined interest schedule. It makes no external calls and requires no DB access for calculation. It is one of the Asset Providers.
๐ User Guide: Scheduled Investment โ User Manual
โ๏ธ Configuration Schema
The asset's value is determined entirely by provider_params, which follow the FAScheduledInvestmentSchedule schema:
| Field | Type | Required | Description |
|---|---|---|---|
initial_value |
Currency |
โ | Principal / face value (e.g., {"code": "EUR", "amount": 10000}) |
interest_type |
SIMPLE \| COMPOUND |
โ | Global interest method (default: SIMPLE) |
day_count |
ACT/365 \| ACT/360 \| ACT/ACT \| 30/360 |
โ | Day count convention (default: ACT/365) |
schedule |
FAInterestRatePeriod[] |
โ | List of interest rate periods (non-overlapping, contiguous) |
late_interest |
FALateInterestConfig |
โ | Optional penalty rate after maturity |
asset_events |
FAAssetEventPoint[] |
โ | Manually defined asset events |
The ScheduledInvestmentEditor shows these as Initial Value / Currency, Interest Type and Day Count; each period row as Period, Rate % (= annual_rate ร 100), Frequency (maturation_frequency) and Generate Coupon (generate_interest). The โก Late Interest switch adds the late row โ grace days are set from its Period cell โ with a Simple/Compound select next to the switch. Asset events are added with Add Event (types Interest, Price Adjustment).
๐ Interest Rate Period (FAInterestRatePeriod)
| Field | Type | Default | Description |
|---|---|---|---|
start_date |
date |
โ | Period start (inclusive) |
end_date |
date |
โ | Period end (inclusive) |
annual_rate |
Decimal |
โ | Annual rate as decimal (e.g., 0.05 for 5%) |
maturation_frequency |
MaturationFrequency |
DAILY |
Emission granularity: DAILY, WEEKLY, MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL |
generate_interest |
bool |
false |
Auto-generate INTEREST events at each maturation date |
๐ Calculation Engine
Price formula:
- SIMPLE:
I = Pโ ร r ร ฮtโ interest always on initial principal - COMPOUND:
I_day = V_{t-1} ร r ร ฮtโ interest on running accumulated value
Emission: Price points are emitted only at maturation dates (not every day), matching the maturation_frequency. Start and end dates of each period always produce a point regardless.
๐ Auto-Generated Events (generate_interest)
When generate_interest = True on a period:
- At each maturation date, the engine checks if accrued interest is positive
- If positive, generates an ๐ต INTEREST event:
value = current_value - initial_value - After the event:
total_interest = 0,event_adjustment = 0โ value resets toinitial_value - Interest accrual restarts from zero on the next day
Coupon Reset Behavior
The INTEREST event represents a "coupon payout" โ the accumulated interest is crystallized and paid out, resetting the running value back to the original principal.
๐ MATURITY_SETTLEMENT Event
Auto-generated when:
- The last schedule period has
generate_interest = Trueand nolate_interestis configured - Or
late_interesthasgenerate_interest = True(settlement at the last late maturation date)
After settlement, the engine is "off" โ get_current_value() returns the settlement value for all future dates.
See Asset Events for the full event type reference.
โฐ Late Interest (FALateInterestConfig)
Applied after the asset's maturity date (last period end_date):
| Field | Type | Default | Description |
|---|---|---|---|
annual_rate |
Decimal |
โ | Annual late interest rate |
grace_period_days |
int |
0 |
Days after maturity before late interest starts |
interest_type |
SIMPLE \| COMPOUND |
COMPOUND |
Late interest compounds by default (penalties grow) |
maturation_frequency |
MaturationFrequency |
DAILY |
Emission frequency for late period |
generate_interest |
bool |
false |
Auto-generate INTEREST + MATURITY_SETTLEMENT events |
During the grace period (from the day after maturity through maturity + grace_period_days) interest keeps accruing at the last schedule period's annual_rate; after it, the late annual_rate applies. Both segments start from the value at maturity and use the late interest_type and the global day_count (_compute_late_interest_value).
Skip Formula Optimization
SIMPLE late interest uses a closed-form calculation for efficiency. COMPOUND late interest uses day-by-day accumulation only within the late sub-period, not from asset start.
โก Caching & Invalidation
The engine caches the full schedule computation as a tuple[dict, list[FAAssetEventPoint]]:
- Key: MD5 of the canonical JSON of the validated schedule (
_cache_key). - Invalidation: Cache is invalidated when
provider_paramschanges (i.e., the user reconfigures the schedule via the frontendScheduledInvestmentEditor). - Hit behavior: On cache hit,
get_history_value()slices the pre-computed dict to the requested date range โ O(1) per point. - Miss behavior: Full schedule recomputation from
initial_valuethrough all periods. This is deterministic and fast (no I/O), typically < 10ms even for multi-year schedules.
The cache is a module-level TTL cache (get_ttl_cache("scheduled_investment"), 256 entries, 48 h TTL), in memory and per process. It survives across multiple sync calls but is cleared on server restart.
๐ง Internal State Variables
During computation, the engine tracks these internal variables per day:
| Variable | Description | Reset behavior |
|---|---|---|
total_interest |
Cumulative accrued interest since last coupon/start | Reset to 0 after INTEREST event |
event_adjustment |
Net sum of PRICE_ADJUSTMENT events | Reset to 0 after INTEREST event |
running_value |
initial_value + total_interest + event_adjustment |
Computed per-day |
These variables are NOT persisted โ they exist only during computation.
๐ Use Cases
- P2P/Crowdfunding Loans: Model a loan with fixed interest rate and periodic coupon payouts.
- Fixed-Rate Bonds: Calculate bond value including accrued interest and coupon payments.
- Any asset with predictable, deterministic cash flows.
๐ Example
A P2P loan of โฌ10,000 with 5% simple annual interest (ACT/365), one period from 1 January, monthly coupons (generate_interest: true). Maturation dates fall on the start date and then every month after it:
- 1 January โ 1 February: value grows from โฌ10,000 to โ โฌ10,042.47 (31 days ร 10,000 ร 5% / 365)
- 1 February (first maturation): ๐ต INTEREST event of โ โฌ42.47 โ value resets to โฌ10,000
- February: value grows again from โฌ10,000
- Maturity: ๐ MATURITY_SETTLEMENT event โ engine stops
๐ง Technical Notes
financial_math.pyeliminated: All financial math functions (calculate_day_count_fraction,calculate_simple_interest,_compute_maturation_dates) are now insidescheduled_investment/plugin.pyas a# Financial Mathsection.identifier_type: AlwaysAUTO_GENERATEDโ the engine generates its own UUID identifier.params_schema: Exposes a singleui_componentfield (key: "_ui_component",default: "scheduled_investment") telling the frontend to render the dedicatedScheduledInvestmentEditorinstead of generic fields.- Events: there is no
supports_eventsflag.get_history_value()returns price points and events together inFAHistoricalData.events(auto-generated + manual, filtered to the requested range).
๐ Related Documentation
- ๐ Scheduled Investment โ User Guide โ End-user configuration guide
- ๐ Asset Events โ Event types, dedup strategy, MATURITY_SETTLEMENT
- ๐ Day Count Conventions โ ACT/365, ACT/360, 30/360, ACT/ACT
- ๐ Asset Types โ CROWDFUND, BOND, etc.
- ๐ฐ Asset Architecture โ Sync pipeline and event persistence
- ๐ฆ Providers Overview โ All available providers