using System;
namespace YLErp.BLL.Calculation.V2
{
///
/// 风险因子种类,用于后续 ValueCalculator 薄接入层签名(CalcPvAtBumpedRiskFactor)。
/// 当前 GreeksBumpCalculator 纯类本身不依赖它,仅作为对外契约的一部分。
///
public enum RiskFactorKind
{
/// 标的价 S
Price,
/// 无风险利率 r
Rate,
/// 波动率 σ
Volatility
}
///
/// 扰动步长规格:决定有限差分用的 ε。
///
/// - Relative:ε = max(|x| * Value, Floor),适合指数等大量级标的,避免绝对 1bp 落入浮点舍入区。
/// - Absolute:ε = Value,等价系统现行“标的价格偏移绝对 1bp”的做法。
///
/// 默认 Floor 极小,仅防止 x=0 时 ε=0 导致除零。
///
public readonly struct BumpSpec
{
/// 步长模式
public enum Mode
{
/// 相对步长(推荐,量级自适应)
Relative,
/// 绝对步长
Absolute
}
/// 步长模式
public Mode Kind { get; }
/// 步长数值(Relative 时为相对比例,Absolute 时为绝对量)
public decimal Value { get; }
/// 相对步长下限(防止 |x| 过小导致 ε→0)
public decimal Floor { get; }
/// 构造步长规格
public BumpSpec(Mode kind, decimal value, decimal floor = 0.00000001m)
{
Kind = kind;
Value = value;
Floor = floor;
}
/// 相对步长(value 为相对比例,如 0.0005m = 0.05%)
public static BumpSpec Relative(decimal value) => new(Mode.Relative, value);
/// 绝对步长(value 为绝对量,如 0.0001m = 1bp)
public static BumpSpec Absolute(decimal value) => new(Mode.Absolute, value);
/// 把规格解析成实际 ε(用 decimal,避免 double 精度漂移)
public decimal Resolve(decimal x)
{
if (Kind == Mode.Absolute) return Value;
var eps = Math.Abs(x) * Value;
return eps < Floor ? Floor : eps;
}
}
///
/// 有限差分希腊字母计算引擎(纯函数,不依赖 QDP / DB)。
///
/// 通过委托 pv(bumpedFactor) 取得“风险因子被扰动到某值时的衍生品价值”,
/// 再用中心差分估算一阶 / 二阶导。这是仓库层对 Greeks 口径的显式控制点,
/// 可绕开 QDP 内部黑盒的步长 / 差分选择,并解决指数类标的使用绝对 1bp 步长失真的口径问题。
///
///
/// 用法:业务侧把“价格 / 利率 / 波动率”各自封装成一个 Func<decimal,decimal> 委托传给本类;
/// Delta/Gamma/Vega/Rho 等命名方法数学上都是同一差分算子,仅扰动的风险因子不同。
///
///
public sealed class GreeksBumpCalculator
{
/// 一阶中心差分 Δ = [PV(x+ε) - PV(x-ε)] / (2ε),误差 O(ε²)
public decimal FirstOrderCentral(Func pv, decimal x, BumpSpec bump)
{
ArgumentNullException.ThrowIfNull(pv);
var eps = bump.Resolve(x);
return (pv(x + eps) - pv(x - eps)) / (2m * eps);
}
/// 二阶中心差分 Γ = [PV(x+ε) - 2·PV(x) + PV(x-ε)] / ε²,误差 O(ε²)
public decimal SecondOrderCentral(Func pv, decimal x, BumpSpec bump)
{
ArgumentNullException.ThrowIfNull(pv);
var eps = bump.Resolve(x);
return (pv(x + eps) - 2m * pv(x) + pv(x - eps)) / (eps * eps);
}
// —— 以下为按业务希腊字母命名的暴露,数学上都是上面两个差分算子,仅扰动因子不同 ——
/// Delta:对标的价 S 的一阶中心差分
public decimal Delta(Func pv, decimal s, BumpSpec bump) => FirstOrderCentral(pv, s, bump);
/// Gamma:对标的价 S 的二阶中心差分
public decimal Gamma(Func pv, decimal s, BumpSpec bump) => SecondOrderCentral(pv, s, bump);
/// Vega:对波动率 σ 的一阶中心差分
public decimal Vega(Func pv, decimal sigma, BumpSpec bump) => FirstOrderCentral(pv, sigma, bump);
/// Vega_r:对波动率 σ 的一阶中心差分(需求命名变体,等价于 Vega)
public decimal VegaR(Func pv, decimal sigma, BumpSpec bump) => FirstOrderCentral(pv, sigma, bump);
/// Rho:对无风险利率 r 的一阶中心差分
public decimal Rho(Func pv, decimal r, BumpSpec bump) => FirstOrderCentral(pv, r, bump);
/// Delta_r:对无风险利率 r 的一阶中心差分(需求命名变体,等价于 Rho)
public decimal DeltaR(Func pv, decimal r, BumpSpec bump) => FirstOrderCentral(pv, r, bump);
/// Gamma_r:对无风险利率 r 的二阶中心差分
public decimal GammaR(Func pv, decimal r, BumpSpec bump) => SecondOrderCentral(pv, r, bump);
///
/// 1BP 变体(Dollar Greek):直接前向 bump 1bp 的 PV 差 = PV(x+1bp) - PV(x),不除 ε,量纲为金额。
/// 对应需求中的 Delta_r(1BP) / Gamma_r(1BP) / Vega_r(1BP)。
///
public decimal BumpPv1Bp(Func pv, decimal x, decimal oneBp = 0.0001m)
{
ArgumentNullException.ThrowIfNull(pv);
return pv(x + oneBp) - pv(x);
}
}
}