feat: strategy builder
This commit is contained in:
31
backend/services/scenario_path.py
Normal file
31
backend/services/scenario_path.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Time-path scenario support: lets a scenario describe an evolving trajectory (bell, range/
|
||||
oscillation, exponential, step, custom points...) for spot shock / IV level / skew tilt /
|
||||
term slope across the days between entry and the scenario horizon, instead of only a single
|
||||
point-in-time shock. The frontend is responsible for turning a shape+params choice into a
|
||||
plain list of {day, value} anchor points (services/lib/scenarioPath.ts) — this module only
|
||||
interpolates whatever anchor points it's given, so it has no notion of "bell" or
|
||||
"oscillation" itself and stays reusable across spot/IV/skew/term alike.
|
||||
|
||||
A path is optional everywhere it's accepted: when None/empty, every call site here falls
|
||||
back to the scalar shock value it already had (unchanged behavior from before paths existed).
|
||||
"""
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
def interpolate_path(path: Optional[List[Dict[str, Any]]], day: float, default: float) -> float:
|
||||
"""Linear interpolation between anchor points {day, value}, clamped flat beyond the
|
||||
first/last anchor. Falls back to `default` when no path is given at all."""
|
||||
if not path:
|
||||
return default
|
||||
pts = sorted(path, key=lambda p: p["day"])
|
||||
if day <= pts[0]["day"]:
|
||||
return pts[0]["value"]
|
||||
if day >= pts[-1]["day"]:
|
||||
return pts[-1]["value"]
|
||||
for p0, p1 in zip(pts, pts[1:]):
|
||||
if p0["day"] <= day <= p1["day"]:
|
||||
span = p1["day"] - p0["day"]
|
||||
w = (day - p0["day"]) / span if span > 1e-9 else 0.0
|
||||
return p0["value"] + (p1["value"] - p0["value"]) * w
|
||||
return pts[-1]["value"]
|
||||
Reference in New Issue
Block a user