using System;
using System.Collections.Generic;
using YLErp.BLL.Calculation.V2.Parameter;
namespace YLErp.BLL.Calculation.V2
{
///
/// 命名风险因子——业界"按因子建模、而非按资产类别建模"的核心抽象。
///
/// 任意衍生品都可视为 PV = Pricer(MarketData)。希腊字母 = 把某个命名风险因子
/// 在 MarketData 上偏移一个标准步长、重定价、再差分。股票/债券指数/债券收益率期权
/// 只是依赖不同的因子(spot / 收益率曲线 / 波动率面),天然被同一套引擎覆盖,
/// 无需为每种资产类别各写一套 Greek 代码。这是 OpenGamma/Strata、QuantLib、FRTB SBA 的同源做法。
///
///
/// 每个因子自带:Id、类别()、标准扰动步长()、
/// 作用目标键(TargetKey)。因子与 ParameterBase 的映射由 完成;
/// 与定价引擎的桥接由 ValueCalculator 薄接入层(CalcPvAtBumpedRiskFactor / PvAsFunctionOf)提供。
///
///
/// 工厂方法 / / 即"步长注册表":
/// 按因子类别给定 FRTB SBA 标准偏移,统一全系统的 ε 与差分口径,解决需求 #2 中
/// "绝对 1bp vs 相对 0.05%"的口径分歧(股指用绝对 1bp 会因量级大落入浮点舍入区,须相对步长)。
///
///
public sealed class RiskFactor
{
/// 因子唯一标识
public string Id { get; }
/// 因子类别(决定施加到 ParameterBase 的哪个字段、用哪种标准步长)
public RiskFactorKind Category { get; }
/// 标准扰动步长(FRTB SBA 口径,由工厂方法按类别给定)
public BumpSpec Shift { get; }
///
/// 作用目标键:Price / Volatility 为 UnderlyingCode;Rate 为曲线/期限标识(如 "CNY-OIS-2Y")。
///
public string TargetKey { get; }
public RiskFactor(string id, RiskFactorKind category, BumpSpec shift, string targetKey)
{
Id = id ?? throw new ArgumentNullException(nameof(id));
Category = category;
Shift = shift;
TargetKey = targetKey ?? string.Empty;
}
// —— 步长注册表:按因子类别给定 FRTB SBA 标准偏移 ——
// 股票 / 指数 spot:相对 1%(指数量级大,绝对 1bp 会落入浮点舍入区,必须相对)
// 利率 / FX:绝对 1bp
// 波动率面:绝对 1bp vol
/// 标的价/指数 spot 因子,默认相对 1% 步长(指数类用此可避免绝对 1bp 失真)
public static RiskFactor Price(string underlyingCode, decimal relativeStep = 0.01m)
=> new($"{underlyingCode}-SPOT", RiskFactorKind.Price, BumpSpec.Relative(relativeStep), underlyingCode);
/// 利率/贴现曲线因子,默认绝对 1bp 步长
public static RiskFactor Rate(string curveTenorId, decimal oneBp = 0.0001m)
=> new($"{curveTenorId}-RATE", RiskFactorKind.Rate, BumpSpec.Absolute(oneBp), curveTenorId);
/// 波动率面因子,默认绝对 1bp vol 步长
public static RiskFactor Volatility(string underlyingCode, decimal oneBp = 0.0001m)
=> new($"{underlyingCode}-VOL", RiskFactorKind.Volatility, BumpSpec.Absolute(oneBp), underlyingCode);
///
/// 把因子扰动到 ,施加到参数副本上(不修改入参)。
/// Rate → RiskFreeRate;Price → SpotPrices[TargetKey];Volatility → cast 期权参数设 Volatility。
///
public void ApplyTo(ParameterBase p, decimal value)
{
if (p == null) throw new ArgumentNullException(nameof(p));
switch (Category)
{
case RiskFactorKind.Rate:
p.RiskFreeRate = (double)value;
break;
case RiskFactorKind.Price:
if (p.SpotPrices == null) p.SpotPrices = new Dictionary();
p.SpotPrices[TargetKey] = (double)value;
break;
case RiskFactorKind.Volatility:
if (p is BaseOptionParameter bo) bo.Volatility = (double)value;
else throw new InvalidOperationException(
$"波动率风险因子[{Id}]只能施加于期权类参数(BaseOptionParameter),当前为 {p.GetType().Name}");
break;
default:
throw new ArgumentOutOfRangeException(nameof(Category), Category, "未支持的风险因子类别");
}
}
///
/// 构造"PV 作为本因子值的函数":克隆 baseParam → 施加扰动值 → 调 reprice 得 PV。
///
/// reprice 生产环境即 ValueCalculator 的真实定价(见 PvAsFunctionOf);测试可注入假函数,无需 DB / QDP。
/// 返回的委托直接喂给 GreeksBumpCalculator 的中心差分方法。
///
///
public Func BuildPvFunction(Func reprice, ParameterBase baseParam)
{
if (reprice == null) throw new ArgumentNullException(nameof(reprice));
if (baseParam == null) throw new ArgumentNullException(nameof(baseParam));
return bumpedValue =>
{
var p = baseParam.Clone();
ApplyTo(p, bumpedValue);
return reprice(p);
};
}
}
}