LP1205's picture
Agentic Due Diligence Intelligence Platform
3ad3d7f
Raw
History Blame Contribute Delete
1.95 kB
"""
Temporal weight calculation β€” Task 2.4 (design doc Section 7).
Risk signals lose relevance over time: a remediated 2019 data breach matters less
than one under active investigation in 2025. ``temporal_weight`` captures this as
a decay factor applied to a signal's effective severity downstream.
Formula (design doc Section 7):
temporal_weight = max(0.3, 1.0 - years_since_event * 0.15)
this year -> 1.00
1 year ago -> 0.85
3 years ago -> 0.55
5+ years ago-> 0.30 (floor β€” a risk is never weighted to zero)
unknown date-> 0.70 (assume "somewhat recent")
"""
from __future__ import annotations
from datetime import date
from typing import Optional
DEFAULT_WEIGHT = 0.7 # data_date is None
MIN_WEIGHT = 0.3 # floor β€” old signals never fully ignored
MAX_WEIGHT = 1.0
DECAY_PER_YEAR = 0.15
_DAYS_PER_YEAR = 365.25
def calculate_temporal_weight(
data_date: Optional[date], *, today: Optional[date] = None
) -> float:
"""Return the temporal decay weight in [0.3, 1.0] for an event on *data_date*.
Args:
data_date: when the underlying event occurred, or None if unknown.
today: reference "now" (defaults to ``date.today()``); injectable so the
calculation is deterministic in tests.
Returns:
``DEFAULT_WEIGHT`` (0.7) when *data_date* is None; otherwise
``max(0.3, 1 - years*0.15)``, clamped to a maximum of 1.0 (future-dated
events are treated as current rather than over-weighted).
"""
if data_date is None:
return DEFAULT_WEIGHT
today = today or date.today()
years = (today - data_date).days / _DAYS_PER_YEAR
if years < 0: # event dated in the future -> treat as current
years = 0.0
return max(MIN_WEIGHT, min(MAX_WEIGHT, 1.0 - years * DECAY_PER_YEAR))
__all__ = [
"calculate_temporal_weight",
"DEFAULT_WEIGHT",
"MIN_WEIGHT",
"MAX_WEIGHT",
"DECAY_PER_YEAR",
]