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